xref: /petsc/src/dm/impls/plex/plex.c (revision e535cce4c56ea8fc2df340b7d14fa8f4f30804fb)
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 PetscBool  Plexcite       = PETSC_FALSE;
18 const char PlexCitation[] = "@article{LangeMitchellKnepleyGorman2015,\n"
19                             "title     = {Efficient mesh management in {Firedrake} using {PETSc-DMPlex}},\n"
20                             "author    = {Michael Lange and Lawrence Mitchell and Matthew G. Knepley and Gerard J. Gorman},\n"
21                             "journal   = {SIAM Journal on Scientific Computing},\n"
22                             "volume    = {38},\n"
23                             "number    = {5},\n"
24                             "pages     = {S143--S155},\n"
25                             "eprint    = {http://arxiv.org/abs/1506.07749},\n"
26                             "doi       = {10.1137/15M1026092},\n"
27                             "year      = {2016},\n"
28                             "petsc_uses={DMPlex},\n}\n";
29 
30 PETSC_EXTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
31 
32 /*@
33   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
34 
35   Input Parameter:
36 . dm - The `DMPLEX` object
37 
38   Output Parameter:
39 . simplex - Flag checking for a simplex
40 
41   Level: intermediate
42 
43   Note:
44   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
45   If the mesh has no cells, this returns `PETSC_FALSE`.
46 
47 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
48 @*/
49 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
50 {
51   DMPolytopeType ct;
52   PetscInt       cStart, cEnd;
53 
54   PetscFunctionBegin;
55   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
56   if (cEnd <= cStart) {
57     *simplex = PETSC_FALSE;
58     PetscFunctionReturn(PETSC_SUCCESS);
59   }
60   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
61   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
62   PetscFunctionReturn(PETSC_SUCCESS);
63 }
64 
65 /*@
66   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
67 
68   Input Parameters:
69 + dm     - The `DMPLEX` object
70 - height - The cell height in the Plex, 0 is the default
71 
72   Output Parameters:
73 + cStart - The first "normal" cell
74 - cEnd   - The upper bound on "normal" cells
75 
76   Level: developer
77 
78   Note:
79   This function requires that tensor cells are ordered last.
80 
81 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetCellTypeStratum()`
82 @*/
83 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd)
84 {
85   DMLabel         ctLabel;
86   IS              valueIS;
87   const PetscInt *ctypes;
88   PetscBool       found = PETSC_FALSE;
89   PetscInt        Nct, cS = PETSC_INT_MAX, cE = 0;
90 
91   PetscFunctionBegin;
92   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
93   PetscCall(DMLabelGetValueIS(ctLabel, &valueIS));
94   PetscCall(ISGetLocalSize(valueIS, &Nct));
95   PetscCall(ISGetIndices(valueIS, &ctypes));
96   for (PetscInt t = 0; t < Nct; ++t) {
97     const DMPolytopeType ct = (DMPolytopeType)ctypes[t];
98     PetscInt             ctS, ctE, ht;
99 
100     if (ct == DM_POLYTOPE_UNKNOWN) {
101       // If any cells are not typed, just use all cells
102       PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), cStart, cEnd));
103       break;
104     }
105     if (DMPolytopeTypeIsHybrid(ct) || ct == DM_POLYTOPE_FV_GHOST) continue;
106     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &ctS, &ctE));
107     if (ctS >= ctE) continue;
108     // Check that a point has the right height
109     PetscCall(DMPlexGetPointHeight(dm, ctS, &ht));
110     if (ht != height) continue;
111     cS    = PetscMin(cS, ctS);
112     cE    = PetscMax(cE, ctE);
113     found = PETSC_TRUE;
114   }
115   if (!Nct || !found) cS = cE = 0;
116   PetscCall(ISDestroy(&valueIS));
117   // Reset label for fast lookup
118   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
119   if (cStart) *cStart = cS;
120   if (cEnd) *cEnd = cE;
121   PetscFunctionReturn(PETSC_SUCCESS);
122 }
123 
124 PetscErrorCode DMPlexGetFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **ssStart, PetscInt **ssEnd, PetscViewerVTKFieldType **sft)
125 {
126   PetscInt                 cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd, c, depth, cellHeight, t;
127   PetscInt                *sStart, *sEnd;
128   PetscViewerVTKFieldType *ft;
129   PetscInt                 vcdof[DM_NUM_POLYTOPES + 1], globalvcdof[DM_NUM_POLYTOPES + 1];
130   DMLabel                  depthLabel, ctLabel;
131 
132   PetscFunctionBegin;
133   /* the vcdof and globalvcdof are sized to allow every polytope type and simple vertex at DM_NUM_POLYTOPES */
134   PetscCall(PetscArrayzero(vcdof, DM_NUM_POLYTOPES + 1));
135   PetscCall(DMGetCoordinateDim(dm, &cdim));
136   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
137   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
138   if (field >= 0) {
139     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[DM_NUM_POLYTOPES]));
140   } else {
141     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[DM_NUM_POLYTOPES]));
142   }
143 
144   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
145   PetscCall(DMPlexGetDepth(dm, &depth));
146   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
147   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
148   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
149     const DMPolytopeType ict = (DMPolytopeType)c;
150     PetscInt             dep;
151 
152     if (ict == DM_POLYTOPE_FV_GHOST) continue;
153     PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
154     if (pStart >= 0) {
155       PetscCall(DMLabelGetValue(depthLabel, cStart, &dep));
156       if (dep != depth - cellHeight) continue;
157     }
158     if (field >= 0) {
159       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[c]));
160     } else {
161       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[c]));
162     }
163   }
164 
165   PetscCallMPI(MPIU_Allreduce(vcdof, globalvcdof, DM_NUM_POLYTOPES + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
166   *types = 0;
167 
168   for (c = 0; c < DM_NUM_POLYTOPES + 1; ++c) {
169     if (globalvcdof[c]) ++(*types);
170   }
171 
172   PetscCall(PetscMalloc3(*types, &sStart, *types, &sEnd, *types, &ft));
173   t = 0;
174   if (globalvcdof[DM_NUM_POLYTOPES]) {
175     sStart[t] = vStart;
176     sEnd[t]   = vEnd;
177     ft[t]     = (globalvcdof[t] == cdim) ? PETSC_VTK_POINT_VECTOR_FIELD : PETSC_VTK_POINT_FIELD;
178     ++t;
179   }
180 
181   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
182     if (globalvcdof[c]) {
183       const DMPolytopeType ict = (DMPolytopeType)c;
184 
185       PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
186       sStart[t] = cStart;
187       sEnd[t]   = cEnd;
188       ft[t]     = (globalvcdof[c] == cdim) ? PETSC_VTK_CELL_VECTOR_FIELD : PETSC_VTK_CELL_FIELD;
189       ++t;
190     }
191   }
192 
193   if (!*types) {
194     if (field >= 0) {
195       const char *fieldname;
196 
197       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
198       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
199     } else {
200       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
201     }
202   }
203 
204   *ssStart = sStart;
205   *ssEnd   = sEnd;
206   *sft     = ft;
207   PetscFunctionReturn(PETSC_SUCCESS);
208 }
209 
210 PetscErrorCode DMPlexRestoreFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **sStart, PetscInt **sEnd, PetscViewerVTKFieldType **ft)
211 {
212   PetscFunctionBegin;
213   PetscCall(PetscFree3(*sStart, *sEnd, *ft));
214   PetscFunctionReturn(PETSC_SUCCESS);
215 }
216 
217 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
218 {
219   PetscInt cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
220   PetscInt vcdof[2] = {0, 0}, globalvcdof[2];
221 
222   PetscFunctionBegin;
223   *ft = PETSC_VTK_INVALID;
224   PetscCall(DMGetCoordinateDim(dm, &cdim));
225   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
226   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
227   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
228   if (field >= 0) {
229     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
230     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
231   } else {
232     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
233     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
234   }
235   PetscCallMPI(MPIU_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
236   if (globalvcdof[0]) {
237     *sStart = vStart;
238     *sEnd   = vEnd;
239     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
240     else *ft = PETSC_VTK_POINT_FIELD;
241   } else if (globalvcdof[1]) {
242     *sStart = cStart;
243     *sEnd   = cEnd;
244     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
245     else *ft = PETSC_VTK_CELL_FIELD;
246   } else {
247     if (field >= 0) {
248       const char *fieldname;
249 
250       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
251       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
252     } else {
253       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
254     }
255   }
256   PetscFunctionReturn(PETSC_SUCCESS);
257 }
258 
259 /*@
260   DMPlexVecView1D - Plot many 1D solutions on the same line graph
261 
262   Collective
263 
264   Input Parameters:
265 + dm     - The `DMPLEX` object
266 . n      - The number of vectors
267 . u      - The array of local vectors
268 - viewer - The `PetscViewer`
269 
270   Level: advanced
271 
272 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `VecViewFromOptions()`, `VecView()`
273 @*/
274 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
275 {
276   PetscDS            ds;
277   PetscDraw          draw = NULL;
278   PetscDrawLG        lg;
279   Vec                coordinates;
280   const PetscScalar *coords, **sol;
281   PetscReal         *vals;
282   PetscInt          *Nc;
283   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
284   char             **names;
285 
286   PetscFunctionBegin;
287   PetscCall(DMGetDS(dm, &ds));
288   PetscCall(PetscDSGetNumFields(ds, &Nf));
289   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
290   PetscCall(PetscDSGetComponents(ds, &Nc));
291 
292   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
293   if (!draw) PetscFunctionReturn(PETSC_SUCCESS);
294   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
295 
296   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
297   for (i = 0, l = 0; i < n; ++i) {
298     const char *vname;
299 
300     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
301     for (f = 0; f < Nf; ++f) {
302       PetscObject disc;
303       const char *fname;
304       char        tmpname[PETSC_MAX_PATH_LEN];
305 
306       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
307       /* TODO Create names for components */
308       for (c = 0; c < Nc[f]; ++c, ++l) {
309         PetscCall(PetscObjectGetName(disc, &fname));
310         PetscCall(PetscStrncpy(tmpname, vname, sizeof(tmpname)));
311         PetscCall(PetscStrlcat(tmpname, ":", sizeof(tmpname)));
312         PetscCall(PetscStrlcat(tmpname, fname, sizeof(tmpname)));
313         PetscCall(PetscStrallocpy(tmpname, &names[l]));
314       }
315     }
316   }
317   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
318   /* Just add P_1 support for now */
319   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
320   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
321   PetscCall(VecGetArrayRead(coordinates, &coords));
322   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
323   for (v = vStart; v < vEnd; ++v) {
324     PetscScalar *x, *svals;
325 
326     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
327     for (i = 0; i < n; ++i) {
328       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
329       for (l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
330     }
331     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
332   }
333   PetscCall(VecRestoreArrayRead(coordinates, &coords));
334   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
335   for (l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
336   PetscCall(PetscFree3(sol, names, vals));
337 
338   PetscCall(PetscDrawLGDraw(lg));
339   PetscCall(PetscDrawLGDestroy(&lg));
340   PetscFunctionReturn(PETSC_SUCCESS);
341 }
342 
343 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
344 {
345   DM dm;
346 
347   PetscFunctionBegin;
348   PetscCall(VecGetDM(u, &dm));
349   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
350   PetscFunctionReturn(PETSC_SUCCESS);
351 }
352 
353 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
354 {
355   DM                 dm;
356   PetscSection       s;
357   PetscDraw          draw, popup;
358   DM                 cdm;
359   PetscSection       coordSection;
360   Vec                coordinates;
361   const PetscScalar *array;
362   PetscReal          lbound[3], ubound[3];
363   PetscReal          vbound[2], time;
364   PetscBool          flg;
365   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
366   const char        *name;
367   char               title[PETSC_MAX_PATH_LEN];
368 
369   PetscFunctionBegin;
370   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
371   PetscCall(VecGetDM(v, &dm));
372   PetscCall(DMGetCoordinateDim(dm, &dim));
373   PetscCall(DMGetLocalSection(dm, &s));
374   PetscCall(PetscSectionGetNumFields(s, &Nf));
375   PetscCall(DMGetCoarsenLevel(dm, &level));
376   PetscCall(DMGetCoordinateDM(dm, &cdm));
377   PetscCall(DMGetLocalSection(cdm, &coordSection));
378   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
379   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
380   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
381 
382   PetscCall(PetscObjectGetName((PetscObject)v, &name));
383   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
384 
385   PetscCall(VecGetLocalSize(coordinates, &N));
386   PetscCall(DMGetBoundingBox(dm, lbound, ubound));
387   PetscCall(PetscDrawClear(draw));
388 
389   /* Could implement something like DMDASelectFields() */
390   for (f = 0; f < Nf; ++f) {
391     DM          fdm = dm;
392     Vec         fv  = v;
393     IS          fis;
394     char        prefix[PETSC_MAX_PATH_LEN];
395     const char *fname;
396 
397     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
398     PetscCall(PetscSectionGetFieldName(s, f, &fname));
399 
400     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
401     else prefix[0] = '\0';
402     if (Nf > 1) {
403       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
404       PetscCall(VecGetSubVector(v, fis, &fv));
405       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
406       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
407     }
408     for (comp = 0; comp < Nc; ++comp, ++w) {
409       PetscInt nmax = 2;
410 
411       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
412       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
413       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
414       PetscCall(PetscDrawSetTitle(draw, title));
415 
416       /* TODO Get max and min only for this component */
417       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
418       if (!flg) {
419         PetscCall(VecMin(fv, NULL, &vbound[0]));
420         PetscCall(VecMax(fv, NULL, &vbound[1]));
421         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
422       }
423 
424       PetscCall(PetscDrawGetPopup(draw, &popup));
425       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
426       PetscCall(PetscDrawSetCoordinates(draw, lbound[0], lbound[1], ubound[0], ubound[1]));
427       PetscCall(VecGetArrayRead(fv, &array));
428       for (c = cStart; c < cEnd; ++c) {
429         DMPolytopeType     ct;
430         PetscScalar       *coords = NULL, *a = NULL;
431         const PetscScalar *coords_arr;
432         PetscBool          isDG;
433         PetscInt           numCoords;
434         int                color[4] = {-1, -1, -1, -1};
435 
436         PetscCall(DMPlexGetCellType(dm, c, &ct));
437         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
438         if (a) {
439           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
440           color[1] = color[2] = color[3] = color[0];
441         } else {
442           PetscScalar *vals = NULL;
443           PetscInt     numVals, va;
444 
445           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
446           if (!numVals) {
447             PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
448             continue;
449           }
450           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);
451           switch (numVals / Nc) {
452           case 1: /* P1 Clamped Segment Prism */
453           case 2: /* P1 Segment Prism, P2 Clamped Segment Prism */
454             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]);
455             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
456             break;
457           case 3: /* P1 Triangle */
458           case 4: /* P1 Quadrangle */
459             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]);
460             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
461             break;
462           case 6: /* P2 Triangle */
463           case 8: /* P2 Quadrangle */
464             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]);
465             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
466             break;
467           default:
468             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
469           }
470           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
471         }
472         PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
473         switch (numCoords) {
474         case 6:
475         case 12: /* Localized triangle */
476           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]));
477           break;
478         case 8:
479         case 16: /* Localized quadrilateral */
480           if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR) {
481             PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscMax(color[0], color[1])));
482           } else {
483             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]));
484             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]));
485           }
486           break;
487         default:
488           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
489         }
490         PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
491       }
492       PetscCall(VecRestoreArrayRead(fv, &array));
493       PetscCall(PetscDrawFlush(draw));
494       PetscCall(PetscDrawPause(draw));
495       PetscCall(PetscDrawSave(draw));
496     }
497     if (Nf > 1) {
498       PetscCall(VecRestoreSubVector(v, fis, &fv));
499       PetscCall(ISDestroy(&fis));
500       PetscCall(DMDestroy(&fdm));
501     }
502   }
503   PetscFunctionReturn(PETSC_SUCCESS);
504 }
505 
506 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
507 {
508   DM        dm;
509   PetscDraw draw;
510   PetscInt  dim;
511   PetscBool isnull;
512 
513   PetscFunctionBegin;
514   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
515   PetscCall(PetscDrawIsNull(draw, &isnull));
516   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
517 
518   PetscCall(VecGetDM(v, &dm));
519   PetscCall(DMGetCoordinateDim(dm, &dim));
520   switch (dim) {
521   case 1:
522     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
523     break;
524   case 2:
525     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
526     break;
527   default:
528     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
529   }
530   PetscFunctionReturn(PETSC_SUCCESS);
531 }
532 
533 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
534 {
535   DM                      dm;
536   Vec                     locv;
537   const char             *name;
538   PetscSection            section;
539   PetscInt                pStart, pEnd;
540   PetscInt                numFields;
541   PetscViewerVTKFieldType ft;
542 
543   PetscFunctionBegin;
544   PetscCall(VecGetDM(v, &dm));
545   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
546   PetscCall(PetscObjectGetName((PetscObject)v, &name));
547   PetscCall(PetscObjectSetName((PetscObject)locv, name));
548   PetscCall(VecCopy(v, locv));
549   PetscCall(DMGetLocalSection(dm, &section));
550   PetscCall(PetscSectionGetNumFields(section, &numFields));
551   if (!numFields) {
552     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
553     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
554   } else {
555     PetscInt f;
556 
557     for (f = 0; f < numFields; f++) {
558       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
559       if (ft == PETSC_VTK_INVALID) continue;
560       PetscCall(PetscObjectReference((PetscObject)locv));
561       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
562     }
563     PetscCall(VecDestroy(&locv));
564   }
565   PetscFunctionReturn(PETSC_SUCCESS);
566 }
567 
568 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
569 {
570   DM        dm;
571   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
572 
573   PetscFunctionBegin;
574   PetscCall(VecGetDM(v, &dm));
575   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
576   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
577   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
578   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
579   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
580   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
581   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
582     PetscInt    i, numFields;
583     PetscObject fe;
584     PetscBool   fem  = PETSC_FALSE;
585     Vec         locv = v;
586     const char *name;
587     PetscInt    step;
588     PetscReal   time;
589 
590     PetscCall(DMGetNumFields(dm, &numFields));
591     for (i = 0; i < numFields; i++) {
592       PetscCall(DMGetField(dm, i, NULL, &fe));
593       if (fe->classid == PETSCFE_CLASSID) {
594         fem = PETSC_TRUE;
595         break;
596       }
597     }
598     if (fem) {
599       PetscObject isZero;
600 
601       PetscCall(DMGetLocalVector(dm, &locv));
602       PetscCall(PetscObjectGetName((PetscObject)v, &name));
603       PetscCall(PetscObjectSetName((PetscObject)locv, name));
604       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
605       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
606       PetscCall(VecCopy(v, locv));
607       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
608       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
609     }
610     if (isvtk) {
611       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
612     } else if (ishdf5) {
613 #if defined(PETSC_HAVE_HDF5)
614       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
615 #else
616       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
617 #endif
618     } else if (isdraw) {
619       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
620     } else if (isglvis) {
621       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
622       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
623       PetscCall(VecView_GLVis(locv, viewer));
624     } else if (iscgns) {
625 #if defined(PETSC_HAVE_CGNS)
626       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
627 #else
628       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
629 #endif
630     }
631     if (fem) {
632       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
633       PetscCall(DMRestoreLocalVector(dm, &locv));
634     }
635   } else {
636     PetscBool isseq;
637 
638     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
639     if (isseq) PetscCall(VecView_Seq(v, viewer));
640     else PetscCall(VecView_MPI(v, viewer));
641   }
642   PetscFunctionReturn(PETSC_SUCCESS);
643 }
644 
645 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
646 {
647   DM        dm;
648   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
649 
650   PetscFunctionBegin;
651   PetscCall(VecGetDM(v, &dm));
652   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
653   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
654   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
655   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
656   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
657   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
658   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
659   if (isvtk || isdraw || isglvis || iscgns) {
660     Vec         locv;
661     PetscObject isZero;
662     const char *name;
663 
664     PetscCall(DMGetLocalVector(dm, &locv));
665     PetscCall(PetscObjectGetName((PetscObject)v, &name));
666     PetscCall(PetscObjectSetName((PetscObject)locv, name));
667     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
668     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
669     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
670     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
671     PetscCall(VecView_Plex_Local(locv, viewer));
672     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
673     PetscCall(DMRestoreLocalVector(dm, &locv));
674     /* Call flush for proper logging of VecView timings */
675     if (isvtk) PetscCall(PetscViewerFlush(viewer));
676   } else if (ishdf5) {
677 #if defined(PETSC_HAVE_HDF5)
678     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
679 #else
680     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
681 #endif
682   } else if (isexodusii) {
683 #if defined(PETSC_HAVE_EXODUSII)
684     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
685 #else
686     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
687 #endif
688   } else {
689     PetscBool isseq;
690 
691     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
692     if (isseq) PetscCall(VecView_Seq(v, viewer));
693     else PetscCall(VecView_MPI(v, viewer));
694   }
695   PetscFunctionReturn(PETSC_SUCCESS);
696 }
697 
698 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
699 {
700   DM                dm;
701   MPI_Comm          comm;
702   PetscViewerFormat format;
703   Vec               v;
704   PetscBool         isvtk, ishdf5;
705 
706   PetscFunctionBegin;
707   PetscCall(VecGetDM(originalv, &dm));
708   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
709   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
710   PetscCall(PetscViewerGetFormat(viewer, &format));
711   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
712   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
713   if (format == PETSC_VIEWER_NATIVE) {
714     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
715     /* this need a better fix */
716     if (dm->useNatural) {
717       if (dm->sfNatural) {
718         const char *vecname;
719         PetscInt    n, nroots;
720 
721         PetscCall(VecGetLocalSize(originalv, &n));
722         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
723         if (n == nroots) {
724           PetscCall(DMPlexCreateNaturalVector(dm, &v));
725           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
726           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
727           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
728           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
729         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
730       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
731     } else v = originalv;
732   } else v = originalv;
733 
734   if (ishdf5) {
735 #if defined(PETSC_HAVE_HDF5)
736     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
737 #else
738     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
739 #endif
740   } else if (isvtk) {
741     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
742   } else {
743     PetscBool isseq;
744 
745     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
746     if (isseq) PetscCall(VecView_Seq(v, viewer));
747     else PetscCall(VecView_MPI(v, viewer));
748   }
749   if (v != originalv) PetscCall(VecDestroy(&v));
750   PetscFunctionReturn(PETSC_SUCCESS);
751 }
752 
753 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
754 {
755   DM        dm;
756   PetscBool ishdf5;
757 
758   PetscFunctionBegin;
759   PetscCall(VecGetDM(v, &dm));
760   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
761   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
762   if (ishdf5) {
763     DM          dmBC;
764     Vec         gv;
765     const char *name;
766 
767     PetscCall(DMGetOutputDM(dm, &dmBC));
768     PetscCall(DMGetGlobalVector(dmBC, &gv));
769     PetscCall(PetscObjectGetName((PetscObject)v, &name));
770     PetscCall(PetscObjectSetName((PetscObject)gv, name));
771     PetscCall(VecLoad_Default(gv, viewer));
772     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
773     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
774     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
775   } else PetscCall(VecLoad_Default(v, viewer));
776   PetscFunctionReturn(PETSC_SUCCESS);
777 }
778 
779 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
780 {
781   DM        dm;
782   PetscBool ishdf5, isexodusii, iscgns;
783 
784   PetscFunctionBegin;
785   PetscCall(VecGetDM(v, &dm));
786   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
787   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
788   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
789   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
790   if (ishdf5) {
791 #if defined(PETSC_HAVE_HDF5)
792     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
793 #else
794     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
795 #endif
796   } else if (isexodusii) {
797 #if defined(PETSC_HAVE_EXODUSII)
798     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
799 #else
800     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
801 #endif
802   } else if (iscgns) {
803 #if defined(PETSC_HAVE_CGNS)
804     PetscCall(VecLoad_Plex_CGNS_Internal(v, viewer));
805 #else
806     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
807 #endif
808   } else PetscCall(VecLoad_Default(v, viewer));
809   PetscFunctionReturn(PETSC_SUCCESS);
810 }
811 
812 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
813 {
814   DM                dm;
815   PetscViewerFormat format;
816   PetscBool         ishdf5;
817 
818   PetscFunctionBegin;
819   PetscCall(VecGetDM(originalv, &dm));
820   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
821   PetscCall(PetscViewerGetFormat(viewer, &format));
822   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
823   if (format == PETSC_VIEWER_NATIVE) {
824     if (dm->useNatural) {
825       if (dm->sfNatural) {
826         if (ishdf5) {
827 #if defined(PETSC_HAVE_HDF5)
828           Vec         v;
829           const char *vecname;
830 
831           PetscCall(DMPlexCreateNaturalVector(dm, &v));
832           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
833           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
834           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
835           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
836           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
837           PetscCall(VecDestroy(&v));
838 #else
839           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
840 #endif
841         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
842       }
843     } else PetscCall(VecLoad_Default(originalv, viewer));
844   }
845   PetscFunctionReturn(PETSC_SUCCESS);
846 }
847 
848 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
849 {
850   PetscSection       coordSection;
851   Vec                coordinates;
852   DMLabel            depthLabel, celltypeLabel;
853   const char        *name[4];
854   const PetscScalar *a;
855   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
856 
857   PetscFunctionBegin;
858   PetscCall(DMGetDimension(dm, &dim));
859   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
860   PetscCall(DMGetCoordinateSection(dm, &coordSection));
861   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
862   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
863   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
864   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
865   PetscCall(VecGetArrayRead(coordinates, &a));
866   name[0]       = "vertex";
867   name[1]       = "edge";
868   name[dim - 1] = "face";
869   name[dim]     = "cell";
870   for (c = cStart; c < cEnd; ++c) {
871     PetscInt *closure = NULL;
872     PetscInt  closureSize, cl, ct;
873 
874     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
875     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
876     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
877     PetscCall(PetscViewerASCIIPushTab(viewer));
878     for (cl = 0; cl < closureSize * 2; cl += 2) {
879       PetscInt point = closure[cl], depth, dof, off, d, p;
880 
881       if ((point < pStart) || (point >= pEnd)) continue;
882       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
883       if (!dof) continue;
884       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
885       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
886       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
887       for (p = 0; p < dof / dim; ++p) {
888         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
889         for (d = 0; d < dim; ++d) {
890           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
891           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
892         }
893         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
894       }
895       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
896     }
897     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
898     PetscCall(PetscViewerASCIIPopTab(viewer));
899   }
900   PetscCall(VecRestoreArrayRead(coordinates, &a));
901   PetscFunctionReturn(PETSC_SUCCESS);
902 }
903 
904 typedef enum {
905   CS_CARTESIAN,
906   CS_POLAR,
907   CS_CYLINDRICAL,
908   CS_SPHERICAL
909 } CoordSystem;
910 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
911 
912 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
913 {
914   PetscInt i;
915 
916   PetscFunctionBegin;
917   if (dim > 3) {
918     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
919   } else {
920     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
921 
922     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
923     switch (cs) {
924     case CS_CARTESIAN:
925       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
926       break;
927     case CS_POLAR:
928       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
929       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
930       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
931       break;
932     case CS_CYLINDRICAL:
933       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
934       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
935       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
936       trcoords[2] = coords[2];
937       break;
938     case CS_SPHERICAL:
939       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
940       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
941       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
942       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
943       break;
944     }
945     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
946   }
947   PetscFunctionReturn(PETSC_SUCCESS);
948 }
949 
950 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
951 {
952   DM_Plex          *mesh = (DM_Plex *)dm->data;
953   DM                cdm, cdmCell;
954   PetscSection      coordSection, coordSectionCell;
955   Vec               coordinates, coordinatesCell;
956   PetscViewerFormat format;
957 
958   PetscFunctionBegin;
959   PetscCall(PetscViewerGetFormat(viewer, &format));
960   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
961     const char *name;
962     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
963     PetscInt    pStart, pEnd, p, numLabels, l;
964     PetscMPIInt rank, size;
965 
966     PetscCall(DMGetCoordinateDM(dm, &cdm));
967     PetscCall(DMGetCoordinateSection(dm, &coordSection));
968     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
969     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
970     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
971     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
972     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
973     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
974     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
975     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
976     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
977     PetscCall(DMGetDimension(dm, &dim));
978     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
979     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
980     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
981     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
982     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
983     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
984     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
985     for (p = pStart; p < pEnd; ++p) {
986       PetscInt dof, off, s;
987 
988       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
989       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
990       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
991     }
992     PetscCall(PetscViewerFlush(viewer));
993     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
994     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
995     for (p = pStart; p < pEnd; ++p) {
996       PetscInt dof, off, c;
997 
998       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
999       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
1000       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]));
1001     }
1002     PetscCall(PetscViewerFlush(viewer));
1003     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1004     if (coordSection && coordinates) {
1005       CoordSystem        cs = CS_CARTESIAN;
1006       const PetscScalar *array, *arrayCell = NULL;
1007       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_INT_MAX, pcEnd = PETSC_INT_MIN, pStart, pEnd, p;
1008       PetscMPIInt        rank;
1009       const char        *name;
1010 
1011       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
1012       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
1013       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
1014       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
1015       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
1016       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
1017       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
1018       pStart = PetscMin(pvStart, pcStart);
1019       pEnd   = PetscMax(pvEnd, pcEnd);
1020       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
1021       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
1022       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
1023       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
1024 
1025       PetscCall(VecGetArrayRead(coordinates, &array));
1026       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
1027       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1028       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
1029       for (p = pStart; p < pEnd; ++p) {
1030         PetscInt dof, off;
1031 
1032         if (p >= pvStart && p < pvEnd) {
1033           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
1034           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
1035           if (dof) {
1036             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dof %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1037             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
1038             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1039           }
1040         }
1041         if (cdmCell && p >= pcStart && p < pcEnd) {
1042           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
1043           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
1044           if (dof) {
1045             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dof %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1046             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
1047             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1048           }
1049         }
1050       }
1051       PetscCall(PetscViewerFlush(viewer));
1052       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1053       PetscCall(VecRestoreArrayRead(coordinates, &array));
1054       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
1055     }
1056     PetscCall(DMGetNumLabels(dm, &numLabels));
1057     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1058     for (l = 0; l < numLabels; ++l) {
1059       DMLabel     label;
1060       PetscBool   isdepth;
1061       const char *name;
1062 
1063       PetscCall(DMGetLabelName(dm, l, &name));
1064       PetscCall(PetscStrcmp(name, "depth", &isdepth));
1065       if (isdepth) continue;
1066       PetscCall(DMGetLabel(dm, name, &label));
1067       PetscCall(DMLabelView(label, viewer));
1068     }
1069     if (size > 1) {
1070       PetscSF sf;
1071 
1072       PetscCall(DMGetPointSF(dm, &sf));
1073       PetscCall(PetscSFView(sf, viewer));
1074     }
1075     if (mesh->periodic.face_sfs)
1076       for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFView(mesh->periodic.face_sfs[i], viewer));
1077     PetscCall(PetscViewerFlush(viewer));
1078   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
1079     const char  *name, *color;
1080     const char  *defcolors[3]  = {"gray", "orange", "green"};
1081     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
1082     char         lname[PETSC_MAX_PATH_LEN];
1083     PetscReal    scale      = 2.0;
1084     PetscReal    tikzscale  = 1.0;
1085     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
1086     double       tcoords[3];
1087     PetscScalar *coords;
1088     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;
1089     PetscMPIInt  rank, size;
1090     char       **names, **colors, **lcolors;
1091     PetscBool    flg, lflg;
1092     PetscBT      wp = NULL;
1093     PetscInt     pEnd, pStart;
1094 
1095     PetscCall(DMGetCoordinateDM(dm, &cdm));
1096     PetscCall(DMGetCoordinateSection(dm, &coordSection));
1097     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1098     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
1099     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
1100     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
1101     PetscCall(DMGetDimension(dm, &dim));
1102     PetscCall(DMPlexGetDepth(dm, &depth));
1103     PetscCall(DMGetNumLabels(dm, &numLabels));
1104     numLabels  = PetscMax(numLabels, 10);
1105     numColors  = 10;
1106     numLColors = 10;
1107     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
1108     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
1109     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
1110     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
1111     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
1112     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
1113     n = 4;
1114     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
1115     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1116     n = 4;
1117     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
1118     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1119     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
1120     if (!useLabels) numLabels = 0;
1121     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
1122     if (!useColors) {
1123       numColors = 3;
1124       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
1125     }
1126     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
1127     if (!useColors) {
1128       numLColors = 4;
1129       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
1130     }
1131     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
1132     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
1133     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
1134     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
1135     if (depth < dim) plotEdges = PETSC_FALSE;
1136     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
1137 
1138     /* filter points with labelvalue != labeldefaultvalue */
1139     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1140     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1141     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1142     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1143     PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
1144     if (lflg) {
1145       DMLabel lbl;
1146 
1147       PetscCall(DMGetLabel(dm, lname, &lbl));
1148       if (lbl) {
1149         PetscInt val, defval;
1150 
1151         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1152         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1153         for (c = pStart; c < pEnd; c++) {
1154           PetscInt *closure = NULL;
1155           PetscInt  closureSize;
1156 
1157           PetscCall(DMLabelGetValue(lbl, c, &val));
1158           if (val == defval) continue;
1159 
1160           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1161           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1162           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1163         }
1164       }
1165     }
1166 
1167     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1168     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1169     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1170     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1171 \\documentclass[tikz]{standalone}\n\n\
1172 \\usepackage{pgflibraryshapes}\n\
1173 \\usetikzlibrary{backgrounds}\n\
1174 \\usetikzlibrary{arrows}\n\
1175 \\begin{document}\n"));
1176     if (size > 1) {
1177       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1178       for (p = 0; p < size; ++p) {
1179         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1180         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1181       }
1182       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1183     }
1184     if (drawHasse) {
1185       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, PetscMax(fEnd - fStart, cEnd - cStart)));
1186 
1187       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1188       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1189       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1190       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1191       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1192       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1193       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1194       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1195       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fStart}{%" PetscInt_FMT "}\n", fStart));
1196       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fEnd}{%" PetscInt_FMT "}\n", fEnd - 1));
1197       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fShift}{%.2f}\n", 3 + (maxStratum - (fEnd - fStart)) / 2.));
1198       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numFaces}{%" PetscInt_FMT "}\n", fEnd - fStart));
1199       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1200       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1201       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1202       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1203     }
1204     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1205 
1206     /* Plot vertices */
1207     PetscCall(VecGetArray(coordinates, &coords));
1208     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1209     for (v = vStart; v < vEnd; ++v) {
1210       PetscInt  off, dof, d;
1211       PetscBool isLabeled = PETSC_FALSE;
1212 
1213       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1214       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1215       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1216       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1217       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1218       for (d = 0; d < dof; ++d) {
1219         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1220         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1221       }
1222       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1223       if (dim == 3) {
1224         PetscReal tmp = tcoords[1];
1225         tcoords[1]    = tcoords[2];
1226         tcoords[2]    = -tmp;
1227       }
1228       for (d = 0; d < dof; ++d) {
1229         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1230         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1231       }
1232       if (drawHasse) color = colors[0 % numColors];
1233       else color = colors[rank % numColors];
1234       for (l = 0; l < numLabels; ++l) {
1235         PetscInt val;
1236         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1237         if (val >= 0) {
1238           color     = lcolors[l % numLColors];
1239           isLabeled = PETSC_TRUE;
1240           break;
1241         }
1242       }
1243       if (drawNumbers[0]) {
1244         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1245       } else if (drawColors[0]) {
1246         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1247       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1248     }
1249     PetscCall(VecRestoreArray(coordinates, &coords));
1250     PetscCall(PetscViewerFlush(viewer));
1251     /* Plot edges */
1252     if (plotEdges) {
1253       PetscCall(VecGetArray(coordinates, &coords));
1254       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1255       for (e = eStart; e < eEnd; ++e) {
1256         const PetscInt *cone;
1257         PetscInt        coneSize, offA, offB, dof, d;
1258 
1259         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1260         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1261         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1262         PetscCall(DMPlexGetCone(dm, e, &cone));
1263         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1264         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1265         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1266         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1267         for (d = 0; d < dof; ++d) {
1268           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1269           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1270         }
1271         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1272         if (dim == 3) {
1273           PetscReal tmp = tcoords[1];
1274           tcoords[1]    = tcoords[2];
1275           tcoords[2]    = -tmp;
1276         }
1277         for (d = 0; d < dof; ++d) {
1278           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1279           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1280         }
1281         if (drawHasse) color = colors[1 % numColors];
1282         else color = colors[rank % numColors];
1283         for (l = 0; l < numLabels; ++l) {
1284           PetscInt val;
1285           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1286           if (val >= 0) {
1287             color = lcolors[l % numLColors];
1288             break;
1289           }
1290         }
1291         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1292       }
1293       PetscCall(VecRestoreArray(coordinates, &coords));
1294       PetscCall(PetscViewerFlush(viewer));
1295       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1296     }
1297     /* Plot cells */
1298     if (dim == 3 || !drawNumbers[1]) {
1299       for (e = eStart; e < eEnd; ++e) {
1300         const PetscInt *cone;
1301 
1302         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1303         color = colors[rank % numColors];
1304         for (l = 0; l < numLabels; ++l) {
1305           PetscInt val;
1306           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1307           if (val >= 0) {
1308             color = lcolors[l % numLColors];
1309             break;
1310           }
1311         }
1312         PetscCall(DMPlexGetCone(dm, e, &cone));
1313         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1314       }
1315     } else {
1316       DMPolytopeType ct;
1317 
1318       /* Drawing a 2D polygon */
1319       for (c = cStart; c < cEnd; ++c) {
1320         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1321         PetscCall(DMPlexGetCellType(dm, c, &ct));
1322         if (DMPolytopeTypeIsHybrid(ct)) {
1323           const PetscInt *cone;
1324           PetscInt        coneSize, e;
1325 
1326           PetscCall(DMPlexGetCone(dm, c, &cone));
1327           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1328           for (e = 0; e < coneSize; ++e) {
1329             const PetscInt *econe;
1330 
1331             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1332             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));
1333           }
1334         } else {
1335           PetscInt *closure = NULL;
1336           PetscInt  closureSize, Nv = 0, v;
1337 
1338           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1339           for (p = 0; p < closureSize * 2; p += 2) {
1340             const PetscInt point = closure[p];
1341 
1342             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1343           }
1344           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1345           for (v = 0; v <= Nv; ++v) {
1346             const PetscInt vertex = closure[v % Nv];
1347 
1348             if (v > 0) {
1349               if (plotEdges) {
1350                 const PetscInt *edge;
1351                 PetscInt        endpoints[2], ne;
1352 
1353                 endpoints[0] = closure[v - 1];
1354                 endpoints[1] = vertex;
1355                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1356                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1357                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1358                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1359               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1360             }
1361             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1362           }
1363           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1364           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1365         }
1366       }
1367     }
1368     for (c = cStart; c < cEnd; ++c) {
1369       double             ccoords[3] = {0.0, 0.0, 0.0};
1370       PetscBool          isLabeled  = PETSC_FALSE;
1371       PetscScalar       *cellCoords = NULL;
1372       const PetscScalar *array;
1373       PetscInt           numCoords, cdim, d;
1374       PetscBool          isDG;
1375 
1376       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1377       PetscCall(DMGetCoordinateDim(dm, &cdim));
1378       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1379       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1380       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1381       for (p = 0; p < numCoords / cdim; ++p) {
1382         for (d = 0; d < cdim; ++d) {
1383           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1384           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1385         }
1386         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1387         if (cdim == 3) {
1388           PetscReal tmp = tcoords[1];
1389           tcoords[1]    = tcoords[2];
1390           tcoords[2]    = -tmp;
1391         }
1392         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1393       }
1394       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1395       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1396       for (d = 0; d < cdim; ++d) {
1397         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1398         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1399       }
1400       if (drawHasse) color = colors[depth % numColors];
1401       else color = colors[rank % numColors];
1402       for (l = 0; l < numLabels; ++l) {
1403         PetscInt val;
1404         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1405         if (val >= 0) {
1406           color     = lcolors[l % numLColors];
1407           isLabeled = PETSC_TRUE;
1408           break;
1409         }
1410       }
1411       if (drawNumbers[dim]) {
1412         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1413       } else if (drawColors[dim]) {
1414         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1415       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1416     }
1417     if (drawHasse) {
1418       int height = 0;
1419 
1420       color = colors[depth % numColors];
1421       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1422       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1423       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1424       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,%d) {\\c};\n", rank, color, height++));
1425       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1426 
1427       if (depth > 2) {
1428         color = colors[1 % numColors];
1429         PetscCall(PetscViewerASCIIPrintf(viewer, "%% Faces\n"));
1430         PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\f in {\\fStart,...,\\fEnd}\n"));
1431         PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1432         PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\f_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\fShift+\\f-\\fStart,%d) {\\f};\n", rank, color, height++));
1433         PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1434       }
1435 
1436       color = colors[1 % numColors];
1437       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1438       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1439       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1440       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,%d) {\\e};\n", rank, color, height++));
1441       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1442 
1443       color = colors[0 % numColors];
1444       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1445       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1446       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1447       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,%d) {\\v};\n", rank, color, height++));
1448       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1449 
1450       for (p = pStart; p < pEnd; ++p) {
1451         const PetscInt *cone;
1452         PetscInt        coneSize, cp;
1453 
1454         PetscCall(DMPlexGetCone(dm, p, &cone));
1455         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1456         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1457       }
1458     }
1459     PetscCall(PetscViewerFlush(viewer));
1460     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1461     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1462     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1463     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1464     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1465     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1466     PetscCall(PetscFree3(names, colors, lcolors));
1467     PetscCall(PetscBTDestroy(&wp));
1468   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1469     Vec                    cown, acown;
1470     VecScatter             sct;
1471     ISLocalToGlobalMapping g2l;
1472     IS                     gid, acis;
1473     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1474     MPI_Group              ggroup, ngroup;
1475     PetscScalar           *array, nid;
1476     const PetscInt        *idxs;
1477     PetscInt              *idxs2, *start, *adjacency, *work;
1478     PetscInt64             lm[3], gm[3];
1479     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1480     PetscMPIInt            d1, d2, rank;
1481 
1482     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1483     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1484 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1485     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1486 #endif
1487     if (ncomm != MPI_COMM_NULL) {
1488       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1489       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1490       d1 = 0;
1491       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1492       nid = d2;
1493       PetscCallMPI(MPI_Group_free(&ggroup));
1494       PetscCallMPI(MPI_Group_free(&ngroup));
1495       PetscCallMPI(MPI_Comm_free(&ncomm));
1496     } else nid = 0.0;
1497 
1498     /* Get connectivity */
1499     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1500     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1501 
1502     /* filter overlapped local cells */
1503     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1504     PetscCall(ISGetIndices(gid, &idxs));
1505     PetscCall(ISGetLocalSize(gid, &cum));
1506     PetscCall(PetscMalloc1(cum, &idxs2));
1507     for (c = cStart, cum = 0; c < cEnd; c++) {
1508       if (idxs[c - cStart] < 0) continue;
1509       idxs2[cum++] = idxs[c - cStart];
1510     }
1511     PetscCall(ISRestoreIndices(gid, &idxs));
1512     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1513     PetscCall(ISDestroy(&gid));
1514     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1515 
1516     /* support for node-aware cell locality */
1517     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1518     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1519     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1520     PetscCall(VecGetArray(cown, &array));
1521     for (c = 0; c < numVertices; c++) array[c] = nid;
1522     PetscCall(VecRestoreArray(cown, &array));
1523     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1524     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1525     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1526     PetscCall(ISDestroy(&acis));
1527     PetscCall(VecScatterDestroy(&sct));
1528     PetscCall(VecDestroy(&cown));
1529 
1530     /* compute edgeCut */
1531     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1532     PetscCall(PetscMalloc1(cum, &work));
1533     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1534     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1535     PetscCall(ISDestroy(&gid));
1536     PetscCall(VecGetArray(acown, &array));
1537     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1538       PetscInt totl;
1539 
1540       totl = start[c + 1] - start[c];
1541       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1542       for (i = 0; i < totl; i++) {
1543         if (work[i] < 0) {
1544           ect += 1;
1545           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1546         }
1547       }
1548     }
1549     PetscCall(PetscFree(work));
1550     PetscCall(VecRestoreArray(acown, &array));
1551     lm[0] = numVertices > 0 ? numVertices : PETSC_INT_MAX;
1552     lm[1] = -numVertices;
1553     PetscCallMPI(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1554     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1555     lm[0] = ect;                     /* edgeCut */
1556     lm[1] = ectn;                    /* node-aware edgeCut */
1557     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1558     PetscCallMPI(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1559     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1560 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1561     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)gm[1]) / ((double)gm[0]) : 1.));
1562 #else
1563     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1564 #endif
1565     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1566     PetscCall(PetscFree(start));
1567     PetscCall(PetscFree(adjacency));
1568     PetscCall(VecDestroy(&acown));
1569   } else {
1570     const char    *name;
1571     PetscInt      *sizes, *hybsizes, *ghostsizes;
1572     PetscInt       locDepth, depth, cellHeight, dim, d;
1573     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1574     PetscInt       numLabels, l, maxSize = 17;
1575     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1576     MPI_Comm       comm;
1577     PetscMPIInt    size, rank;
1578 
1579     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1580     PetscCallMPI(MPI_Comm_size(comm, &size));
1581     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1582     PetscCall(DMGetDimension(dm, &dim));
1583     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1584     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1585     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1586     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1587     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1588     PetscCall(DMPlexGetDepth(dm, &locDepth));
1589     PetscCallMPI(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1590     PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &gcStart, &gcEnd));
1591     gcNum = gcEnd - gcStart;
1592     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1593     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1594     for (d = 0; d <= depth; d++) {
1595       PetscInt Nc[2] = {0, 0}, ict;
1596 
1597       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1598       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1599       ict = ct0;
1600       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1601       ct0 = (DMPolytopeType)ict;
1602       for (p = pStart; p < pEnd; ++p) {
1603         DMPolytopeType ct;
1604 
1605         PetscCall(DMPlexGetCellType(dm, p, &ct));
1606         if (ct == ct0) ++Nc[0];
1607         else ++Nc[1];
1608       }
1609       if (size < maxSize) {
1610         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1611         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1612         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1613         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1614         for (p = 0; p < size; ++p) {
1615           if (rank == 0) {
1616             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1617             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1618             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1619           }
1620         }
1621       } else {
1622         PetscInt locMinMax[2];
1623 
1624         locMinMax[0] = Nc[0] + Nc[1];
1625         locMinMax[1] = Nc[0] + Nc[1];
1626         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1627         locMinMax[0] = Nc[1];
1628         locMinMax[1] = Nc[1];
1629         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1630         if (d == depth) {
1631           locMinMax[0] = gcNum;
1632           locMinMax[1] = gcNum;
1633           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1634         }
1635         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1636         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1637         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1638         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1639       }
1640       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1641     }
1642     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1643     {
1644       const PetscReal *maxCell;
1645       const PetscReal *L;
1646       PetscBool        localized;
1647 
1648       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1649       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1650       if (L || localized) {
1651         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1652         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1653         if (L) {
1654           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1655           for (d = 0; d < dim; ++d) {
1656             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1657             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1658           }
1659           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1660         }
1661         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1662         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1663       }
1664     }
1665     PetscCall(DMGetNumLabels(dm, &numLabels));
1666     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1667     for (l = 0; l < numLabels; ++l) {
1668       DMLabel     label;
1669       const char *name;
1670       PetscInt   *values;
1671       PetscInt    numValues, v;
1672 
1673       PetscCall(DMGetLabelName(dm, l, &name));
1674       PetscCall(DMGetLabel(dm, name, &label));
1675       PetscCall(DMLabelGetNumValues(label, &numValues));
1676       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1677 
1678       { // Extract array of DMLabel values so it can be sorted
1679         IS              is_values;
1680         const PetscInt *is_values_local = NULL;
1681 
1682         PetscCall(DMLabelGetValueIS(label, &is_values));
1683         PetscCall(ISGetIndices(is_values, &is_values_local));
1684         PetscCall(PetscMalloc1(numValues, &values));
1685         PetscCall(PetscArraycpy(values, is_values_local, numValues));
1686         PetscCall(PetscSortInt(numValues, values));
1687         PetscCall(ISRestoreIndices(is_values, &is_values_local));
1688         PetscCall(ISDestroy(&is_values));
1689       }
1690       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1691       for (v = 0; v < numValues; ++v) {
1692         PetscInt size;
1693 
1694         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1695         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1696         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1697       }
1698       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1699       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1700       PetscCall(PetscFree(values));
1701     }
1702     {
1703       char    **labelNames;
1704       PetscInt  Nl = numLabels;
1705       PetscBool flg;
1706 
1707       PetscCall(PetscMalloc1(Nl, &labelNames));
1708       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1709       for (l = 0; l < Nl; ++l) {
1710         DMLabel label;
1711 
1712         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1713         if (flg) {
1714           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1715           PetscCall(DMLabelView(label, viewer));
1716         }
1717         PetscCall(PetscFree(labelNames[l]));
1718       }
1719       PetscCall(PetscFree(labelNames));
1720     }
1721     /* If no fields are specified, people do not want to see adjacency */
1722     if (dm->Nf) {
1723       PetscInt f;
1724 
1725       for (f = 0; f < dm->Nf; ++f) {
1726         const char *name;
1727 
1728         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1729         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1730         PetscCall(PetscViewerASCIIPushTab(viewer));
1731         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1732         if (dm->fields[f].adjacency[0]) {
1733           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1734           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1735         } else {
1736           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1737           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1738         }
1739         PetscCall(PetscViewerASCIIPopTab(viewer));
1740       }
1741     }
1742     PetscCall(DMGetCoarseDM(dm, &cdm));
1743     if (cdm) {
1744       PetscCall(PetscViewerASCIIPushTab(viewer));
1745       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1746       PetscCall(DMPlexView_Ascii(cdm, viewer));
1747       PetscCall(PetscViewerASCIIPopTab(viewer));
1748     }
1749   }
1750   PetscFunctionReturn(PETSC_SUCCESS);
1751 }
1752 
1753 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1754 {
1755   DMPolytopeType ct;
1756   PetscMPIInt    rank;
1757   PetscInt       cdim;
1758 
1759   PetscFunctionBegin;
1760   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1761   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1762   PetscCall(DMGetCoordinateDim(dm, &cdim));
1763   switch (ct) {
1764   case DM_POLYTOPE_SEGMENT:
1765   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1766     switch (cdim) {
1767     case 1: {
1768       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1769       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1770 
1771       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1772       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1773       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1774     } break;
1775     case 2: {
1776       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1777       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1778       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1779 
1780       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1781       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));
1782       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));
1783     } break;
1784     default:
1785       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1786     }
1787     break;
1788   case DM_POLYTOPE_TRIANGLE:
1789     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));
1790     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1791     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1792     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1793     break;
1794   case DM_POLYTOPE_QUADRILATERAL:
1795     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));
1796     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));
1797     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1798     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1799     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1800     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1801     break;
1802   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1803     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));
1804     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));
1805     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1806     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1807     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1808     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1809     break;
1810   case DM_POLYTOPE_FV_GHOST:
1811     break;
1812   default:
1813     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1814   }
1815   PetscFunctionReturn(PETSC_SUCCESS);
1816 }
1817 
1818 static PetscErrorCode DrawPolygon_Private(DM dm, PetscDraw draw, PetscInt cell, PetscInt Nv, const PetscReal refVertices[], const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1819 {
1820   PetscReal   centroid[2] = {0., 0.};
1821   PetscMPIInt rank;
1822   PetscMPIInt fillColor;
1823 
1824   PetscFunctionBegin;
1825   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1826   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1827   for (PetscInt v = 0; v < Nv; ++v) {
1828     centroid[0] += PetscRealPart(coords[v * 2 + 0]) / Nv;
1829     centroid[1] += PetscRealPart(coords[v * 2 + 1]) / Nv;
1830   }
1831   for (PetscInt e = 0; e < Nv; ++e) {
1832     refCoords[0] = refVertices[e * 2 + 0];
1833     refCoords[1] = refVertices[e * 2 + 1];
1834     for (PetscInt d = 1; d <= edgeDiv; ++d) {
1835       refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % Nv * 2 + 0] - refCoords[0]) * d / edgeDiv;
1836       refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % Nv * 2 + 1] - refCoords[1]) * d / edgeDiv;
1837     }
1838     PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1839     for (PetscInt d = 0; d < edgeDiv; ++d) {
1840       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));
1841       PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1842     }
1843   }
1844   PetscFunctionReturn(PETSC_SUCCESS);
1845 }
1846 
1847 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1848 {
1849   DMPolytopeType ct;
1850 
1851   PetscFunctionBegin;
1852   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1853   switch (ct) {
1854   case DM_POLYTOPE_TRIANGLE: {
1855     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1856 
1857     PetscCall(DrawPolygon_Private(dm, draw, cell, 3, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1858   } break;
1859   case DM_POLYTOPE_QUADRILATERAL: {
1860     PetscReal refVertices[8] = {-1., -1., 1., -1., 1., 1., -1., 1.};
1861 
1862     PetscCall(DrawPolygon_Private(dm, draw, cell, 4, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1863   } break;
1864   default:
1865     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1866   }
1867   PetscFunctionReturn(PETSC_SUCCESS);
1868 }
1869 
1870 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1871 {
1872   PetscDraw    draw;
1873   DM           cdm;
1874   PetscSection coordSection;
1875   Vec          coordinates;
1876   PetscReal    xyl[3], xyr[3];
1877   PetscReal   *refCoords, *edgeCoords;
1878   PetscBool    isnull, drawAffine;
1879   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, cDegree, edgeDiv;
1880 
1881   PetscFunctionBegin;
1882   PetscCall(DMGetCoordinateDim(dm, &dim));
1883   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1884   PetscCall(DMGetCoordinateDegree_Internal(dm, &cDegree));
1885   drawAffine = cDegree > 1 ? PETSC_FALSE : PETSC_TRUE;
1886   edgeDiv    = cDegree + 1;
1887   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1888   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1889   PetscCall(DMGetCoordinateDM(dm, &cdm));
1890   PetscCall(DMGetLocalSection(cdm, &coordSection));
1891   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1892   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1893   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1894 
1895   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1896   PetscCall(PetscDrawIsNull(draw, &isnull));
1897   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1898   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1899 
1900   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1901   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1902   PetscCall(PetscDrawClear(draw));
1903 
1904   for (c = cStart; c < cEnd; ++c) {
1905     PetscScalar       *coords = NULL;
1906     const PetscScalar *coords_arr;
1907     PetscInt           numCoords;
1908     PetscBool          isDG;
1909 
1910     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1911     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1912     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1913     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1914   }
1915   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1916   PetscCall(PetscDrawFlush(draw));
1917   PetscCall(PetscDrawPause(draw));
1918   PetscCall(PetscDrawSave(draw));
1919   PetscFunctionReturn(PETSC_SUCCESS);
1920 }
1921 
1922 static PetscErrorCode DMPlexCreateHighOrderSurrogate_Internal(DM dm, DM *hdm)
1923 {
1924   DM           odm = dm, rdm = dm, cdm;
1925   PetscFE      fe;
1926   PetscSpace   sp;
1927   PetscClassId id;
1928   PetscInt     degree;
1929   PetscBool    hoView = PETSC_TRUE;
1930 
1931   PetscFunctionBegin;
1932   PetscObjectOptionsBegin((PetscObject)dm);
1933   PetscCall(PetscOptionsBool("-dm_plex_high_order_view", "Subsample to view meshes with high order coordinates", "DMPlexCreateHighOrderSurrogate_Internal", hoView, &hoView, NULL));
1934   PetscOptionsEnd();
1935   PetscCall(PetscObjectReference((PetscObject)dm));
1936   *hdm = dm;
1937   if (!hoView) PetscFunctionReturn(PETSC_SUCCESS);
1938   PetscCall(DMGetCoordinateDM(dm, &cdm));
1939   PetscCall(DMGetField(cdm, 0, NULL, (PetscObject *)&fe));
1940   PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
1941   if (id != PETSCFE_CLASSID) PetscFunctionReturn(PETSC_SUCCESS);
1942   PetscCall(PetscFEGetBasisSpace(fe, &sp));
1943   PetscCall(PetscSpaceGetDegree(sp, &degree, NULL));
1944   for (PetscInt r = 0, rd = PetscCeilReal(((PetscReal)degree) / 2.); r < (PetscInt)PetscCeilReal(PetscLog2Real(degree)); ++r, rd = PetscCeilReal(((PetscReal)rd) / 2.)) {
1945     DM  cdm, rcdm;
1946     Mat In;
1947     Vec cl, rcl;
1948 
1949     PetscCall(DMRefine(odm, PetscObjectComm((PetscObject)odm), &rdm));
1950     PetscCall(DMPlexCreateCoordinateSpace(rdm, rd, PETSC_FALSE, NULL));
1951     PetscCall(PetscObjectSetName((PetscObject)rdm, "Refined Mesh with Linear Coordinates"));
1952     PetscCall(DMGetCoordinateDM(odm, &cdm));
1953     PetscCall(DMGetCoordinateDM(rdm, &rcdm));
1954     PetscCall(DMGetCoordinatesLocal(odm, &cl));
1955     PetscCall(DMGetCoordinatesLocal(rdm, &rcl));
1956     PetscCall(DMSetCoarseDM(rcdm, cdm));
1957     PetscCall(DMCreateInterpolation(cdm, rcdm, &In, NULL));
1958     PetscCall(MatMult(In, cl, rcl));
1959     PetscCall(MatDestroy(&In));
1960     PetscCall(DMSetCoordinatesLocal(rdm, rcl));
1961     PetscCall(DMDestroy(&odm));
1962     odm = rdm;
1963   }
1964   *hdm = rdm;
1965   PetscFunctionReturn(PETSC_SUCCESS);
1966 }
1967 
1968 #if defined(PETSC_HAVE_EXODUSII)
1969   #include <exodusII.h>
1970   #include <petscviewerexodusii.h>
1971 #endif
1972 
1973 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1974 {
1975   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns, ispython;
1976   char      name[PETSC_MAX_PATH_LEN];
1977 
1978   PetscFunctionBegin;
1979   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1980   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1981   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1982   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1983   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1984   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1985   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1986   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1987   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1988   PetscCall(PetscObjectHasFunction((PetscObject)viewer, "PetscViewerPythonViewObject_C", &ispython));
1989   if (iascii) {
1990     PetscViewerFormat format;
1991     PetscCall(PetscViewerGetFormat(viewer, &format));
1992     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1993     else PetscCall(DMPlexView_Ascii(dm, viewer));
1994   } else if (ishdf5) {
1995 #if defined(PETSC_HAVE_HDF5)
1996     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1997 #else
1998     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1999 #endif
2000   } else if (isvtk) {
2001     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
2002   } else if (isdraw) {
2003     DM hdm;
2004 
2005     PetscCall(DMPlexCreateHighOrderSurrogate_Internal(dm, &hdm));
2006     PetscCall(DMPlexView_Draw(hdm, viewer));
2007     PetscCall(DMDestroy(&hdm));
2008   } else if (isglvis) {
2009     PetscCall(DMPlexView_GLVis(dm, viewer));
2010 #if defined(PETSC_HAVE_EXODUSII)
2011   } else if (isexodus) {
2012     /*
2013       exodusII requires that all sets be part of exactly one cell set.
2014       If the dm does not have a "Cell Sets" label defined, we create one
2015       with ID 1, containing all cells.
2016       Note that if the Cell Sets label is defined but does not cover all cells,
2017       we may still have a problem. This should probably be checked here or in the viewer;
2018     */
2019     PetscInt numCS;
2020     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
2021     if (!numCS) {
2022       PetscInt cStart, cEnd, c;
2023       PetscCall(DMCreateLabel(dm, "Cell Sets"));
2024       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
2025       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
2026     }
2027     PetscCall(DMView_PlexExodusII(dm, viewer));
2028 #endif
2029 #if defined(PETSC_HAVE_CGNS)
2030   } else if (iscgns) {
2031     PetscCall(DMView_PlexCGNS(dm, viewer));
2032 #endif
2033   } else if (ispython) {
2034     PetscCall(PetscViewerPythonViewObject(viewer, (PetscObject)dm));
2035   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
2036   /* Optionally view the partition */
2037   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
2038   if (flg) {
2039     Vec ranks;
2040     PetscCall(DMPlexCreateRankField(dm, &ranks));
2041     PetscCall(VecView(ranks, viewer));
2042     PetscCall(VecDestroy(&ranks));
2043   }
2044   /* Optionally view a label */
2045   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
2046   if (flg) {
2047     DMLabel label;
2048     Vec     val;
2049 
2050     PetscCall(DMGetLabel(dm, name, &label));
2051     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
2052     PetscCall(DMPlexCreateLabelField(dm, label, &val));
2053     PetscCall(VecView(val, viewer));
2054     PetscCall(VecDestroy(&val));
2055   }
2056   PetscFunctionReturn(PETSC_SUCCESS);
2057 }
2058 
2059 /*@
2060   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
2061 
2062   Collective
2063 
2064   Input Parameters:
2065 + dm     - The `DM` whose topology is to be saved
2066 - viewer - The `PetscViewer` to save it in
2067 
2068   Level: advanced
2069 
2070 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
2071 @*/
2072 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
2073 {
2074   PetscBool ishdf5;
2075 
2076   PetscFunctionBegin;
2077   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2078   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2079   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2080   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
2081   if (ishdf5) {
2082 #if defined(PETSC_HAVE_HDF5)
2083     PetscViewerFormat format;
2084     PetscCall(PetscViewerGetFormat(viewer, &format));
2085     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2086       IS globalPointNumbering;
2087 
2088       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2089       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
2090       PetscCall(ISDestroy(&globalPointNumbering));
2091     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2092 #else
2093     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2094 #endif
2095   }
2096   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
2097   PetscFunctionReturn(PETSC_SUCCESS);
2098 }
2099 
2100 /*@
2101   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
2102 
2103   Collective
2104 
2105   Input Parameters:
2106 + dm     - The `DM` whose coordinates are to be saved
2107 - viewer - The `PetscViewer` for saving
2108 
2109   Level: advanced
2110 
2111 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
2112 @*/
2113 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
2114 {
2115   PetscBool ishdf5;
2116 
2117   PetscFunctionBegin;
2118   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2119   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2120   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2121   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2122   if (ishdf5) {
2123 #if defined(PETSC_HAVE_HDF5)
2124     PetscViewerFormat format;
2125     PetscCall(PetscViewerGetFormat(viewer, &format));
2126     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2127       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
2128     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2129 #else
2130     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2131 #endif
2132   }
2133   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2134   PetscFunctionReturn(PETSC_SUCCESS);
2135 }
2136 
2137 /*@
2138   DMPlexLabelsView - Saves `DMPLEX` labels into a file
2139 
2140   Collective
2141 
2142   Input Parameters:
2143 + dm     - The `DM` whose labels are to be saved
2144 - viewer - The `PetscViewer` for saving
2145 
2146   Level: advanced
2147 
2148 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
2149 @*/
2150 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
2151 {
2152   PetscBool ishdf5;
2153 
2154   PetscFunctionBegin;
2155   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2156   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2157   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2158   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
2159   if (ishdf5) {
2160 #if defined(PETSC_HAVE_HDF5)
2161     IS                globalPointNumbering;
2162     PetscViewerFormat format;
2163 
2164     PetscCall(PetscViewerGetFormat(viewer, &format));
2165     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2166       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2167       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
2168       PetscCall(ISDestroy(&globalPointNumbering));
2169     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2170 #else
2171     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2172 #endif
2173   }
2174   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
2175   PetscFunctionReturn(PETSC_SUCCESS);
2176 }
2177 
2178 /*@
2179   DMPlexSectionView - Saves a section associated with a `DMPLEX`
2180 
2181   Collective
2182 
2183   Input Parameters:
2184 + dm        - The `DM` that contains the topology on which the section to be saved is defined
2185 . viewer    - The `PetscViewer` for saving
2186 - sectiondm - The `DM` that contains the section to be saved, can be `NULL`
2187 
2188   Level: advanced
2189 
2190   Notes:
2191   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.
2192 
2193   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.
2194 
2195 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
2196 @*/
2197 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
2198 {
2199   PetscBool ishdf5;
2200 
2201   PetscFunctionBegin;
2202   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2203   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2204   if (!sectiondm) sectiondm = dm;
2205   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2206   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2207   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
2208   if (ishdf5) {
2209 #if defined(PETSC_HAVE_HDF5)
2210     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
2211 #else
2212     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2213 #endif
2214   }
2215   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
2216   PetscFunctionReturn(PETSC_SUCCESS);
2217 }
2218 
2219 /*@
2220   DMPlexGlobalVectorView - Saves a global vector
2221 
2222   Collective
2223 
2224   Input Parameters:
2225 + dm        - The `DM` that represents the topology
2226 . viewer    - The `PetscViewer` to save data with
2227 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2228 - vec       - The global vector to be saved
2229 
2230   Level: advanced
2231 
2232   Notes:
2233   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.
2234 
2235   Calling sequence:
2236 .vb
2237        DMCreate(PETSC_COMM_WORLD, &dm);
2238        DMSetType(dm, DMPLEX);
2239        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2240        DMClone(dm, &sectiondm);
2241        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2242        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2243        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2244        PetscSectionSetChart(section, pStart, pEnd);
2245        PetscSectionSetUp(section);
2246        DMSetLocalSection(sectiondm, section);
2247        PetscSectionDestroy(&section);
2248        DMGetGlobalVector(sectiondm, &vec);
2249        PetscObjectSetName((PetscObject)vec, "vec_name");
2250        DMPlexTopologyView(dm, viewer);
2251        DMPlexSectionView(dm, viewer, sectiondm);
2252        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2253        DMRestoreGlobalVector(sectiondm, &vec);
2254        DMDestroy(&sectiondm);
2255        DMDestroy(&dm);
2256 .ve
2257 
2258 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2259 @*/
2260 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2261 {
2262   PetscBool ishdf5;
2263 
2264   PetscFunctionBegin;
2265   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2266   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2267   if (!sectiondm) sectiondm = dm;
2268   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2269   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2270   /* Check consistency */
2271   {
2272     PetscSection section;
2273     PetscBool    includesConstraints;
2274     PetscInt     m, m1;
2275 
2276     PetscCall(VecGetLocalSize(vec, &m1));
2277     PetscCall(DMGetGlobalSection(sectiondm, &section));
2278     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2279     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2280     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2281     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2282   }
2283   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2284   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2285   if (ishdf5) {
2286 #if defined(PETSC_HAVE_HDF5)
2287     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2288 #else
2289     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2290 #endif
2291   }
2292   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2293   PetscFunctionReturn(PETSC_SUCCESS);
2294 }
2295 
2296 /*@
2297   DMPlexLocalVectorView - Saves a local vector
2298 
2299   Collective
2300 
2301   Input Parameters:
2302 + dm        - The `DM` that represents the topology
2303 . viewer    - The `PetscViewer` to save data with
2304 . sectiondm - The `DM` that contains the local section on which `vec` is defined, can be `NULL`
2305 - vec       - The local vector to be saved
2306 
2307   Level: advanced
2308 
2309   Note:
2310   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2311 
2312   Calling sequence:
2313 .vb
2314        DMCreate(PETSC_COMM_WORLD, &dm);
2315        DMSetType(dm, DMPLEX);
2316        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2317        DMClone(dm, &sectiondm);
2318        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2319        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2320        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2321        PetscSectionSetChart(section, pStart, pEnd);
2322        PetscSectionSetUp(section);
2323        DMSetLocalSection(sectiondm, section);
2324        DMGetLocalVector(sectiondm, &vec);
2325        PetscObjectSetName((PetscObject)vec, "vec_name");
2326        DMPlexTopologyView(dm, viewer);
2327        DMPlexSectionView(dm, viewer, sectiondm);
2328        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2329        DMRestoreLocalVector(sectiondm, &vec);
2330        DMDestroy(&sectiondm);
2331        DMDestroy(&dm);
2332 .ve
2333 
2334 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2335 @*/
2336 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2337 {
2338   PetscBool ishdf5;
2339 
2340   PetscFunctionBegin;
2341   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2342   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2343   if (!sectiondm) sectiondm = dm;
2344   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2345   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2346   /* Check consistency */
2347   {
2348     PetscSection section;
2349     PetscBool    includesConstraints;
2350     PetscInt     m, m1;
2351 
2352     PetscCall(VecGetLocalSize(vec, &m1));
2353     PetscCall(DMGetLocalSection(sectiondm, &section));
2354     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2355     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2356     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2357     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2358   }
2359   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2360   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2361   if (ishdf5) {
2362 #if defined(PETSC_HAVE_HDF5)
2363     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2364 #else
2365     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2366 #endif
2367   }
2368   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2369   PetscFunctionReturn(PETSC_SUCCESS);
2370 }
2371 
2372 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2373 {
2374   PetscBool ishdf5;
2375 
2376   PetscFunctionBegin;
2377   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2378   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2379   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2380   if (ishdf5) {
2381 #if defined(PETSC_HAVE_HDF5)
2382     PetscViewerFormat format;
2383     PetscCall(PetscViewerGetFormat(viewer, &format));
2384     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2385       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2386     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2387       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2388     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2389     PetscFunctionReturn(PETSC_SUCCESS);
2390 #else
2391     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2392 #endif
2393   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2394 }
2395 
2396 /*@
2397   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2398 
2399   Collective
2400 
2401   Input Parameters:
2402 + dm     - The `DM` into which the topology is loaded
2403 - viewer - The `PetscViewer` for the saved topology
2404 
2405   Output Parameter:
2406 . 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;
2407   `NULL` if unneeded
2408 
2409   Level: advanced
2410 
2411 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2412           `PetscViewer`, `PetscSF`
2413 @*/
2414 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2415 {
2416   PetscBool ishdf5;
2417 
2418   PetscFunctionBegin;
2419   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2420   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2421   if (globalToLocalPointSF) PetscAssertPointer(globalToLocalPointSF, 3);
2422   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2423   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2424   if (ishdf5) {
2425 #if defined(PETSC_HAVE_HDF5)
2426     PetscViewerFormat format;
2427     PetscCall(PetscViewerGetFormat(viewer, &format));
2428     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2429       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2430     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2431 #else
2432     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2433 #endif
2434   }
2435   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2436   PetscFunctionReturn(PETSC_SUCCESS);
2437 }
2438 
2439 /*@
2440   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2441 
2442   Collective
2443 
2444   Input Parameters:
2445 + dm                   - The `DM` into which the coordinates are loaded
2446 . viewer               - The `PetscViewer` for the saved coordinates
2447 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2448 
2449   Level: advanced
2450 
2451 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2452           `PetscSF`, `PetscViewer`
2453 @*/
2454 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2455 {
2456   PetscBool ishdf5;
2457 
2458   PetscFunctionBegin;
2459   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2460   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2461   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2462   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2463   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2464   if (ishdf5) {
2465 #if defined(PETSC_HAVE_HDF5)
2466     PetscViewerFormat format;
2467     PetscCall(PetscViewerGetFormat(viewer, &format));
2468     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2469       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2470     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2471 #else
2472     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2473 #endif
2474   }
2475   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2476   PetscFunctionReturn(PETSC_SUCCESS);
2477 }
2478 
2479 /*@
2480   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2481 
2482   Collective
2483 
2484   Input Parameters:
2485 + dm                   - The `DM` into which the labels are loaded
2486 . viewer               - The `PetscViewer` for the saved labels
2487 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2488 
2489   Level: advanced
2490 
2491   Note:
2492   The `PetscSF` argument must not be `NULL` if the `DM` is distributed, otherwise an error occurs.
2493 
2494 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2495           `PetscSF`, `PetscViewer`
2496 @*/
2497 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2498 {
2499   PetscBool ishdf5;
2500 
2501   PetscFunctionBegin;
2502   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2503   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2504   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2505   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2506   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2507   if (ishdf5) {
2508 #if defined(PETSC_HAVE_HDF5)
2509     PetscViewerFormat format;
2510 
2511     PetscCall(PetscViewerGetFormat(viewer, &format));
2512     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2513       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2514     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2515 #else
2516     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2517 #endif
2518   }
2519   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2520   PetscFunctionReturn(PETSC_SUCCESS);
2521 }
2522 
2523 /*@
2524   DMPlexSectionLoad - Loads section into a `DMPLEX`
2525 
2526   Collective
2527 
2528   Input Parameters:
2529 + dm                   - The `DM` that represents the topology
2530 . viewer               - The `PetscViewer` that represents the on-disk section (sectionA)
2531 . sectiondm            - The `DM` into which the on-disk section (sectionA) is migrated, can be `NULL`
2532 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2533 
2534   Output Parameters:
2535 + 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)
2536 - 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)
2537 
2538   Level: advanced
2539 
2540   Notes:
2541   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.
2542 
2543   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.
2544 
2545   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.
2546 
2547   Example using 2 processes:
2548 .vb
2549   NX (number of points on dm): 4
2550   sectionA                   : the on-disk section
2551   vecA                       : a vector associated with sectionA
2552   sectionB                   : sectiondm's local section constructed in this function
2553   vecB (local)               : a vector associated with sectiondm's local section
2554   vecB (global)              : a vector associated with sectiondm's global section
2555 
2556                                      rank 0    rank 1
2557   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2558   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2559   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2560   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2561   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2562   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2563   sectionB->atlasDof             :     1 0 1 | 1 3
2564   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2565   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2566   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2567 .ve
2568   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2569 
2570 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2571 @*/
2572 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2573 {
2574   PetscBool ishdf5;
2575 
2576   PetscFunctionBegin;
2577   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2578   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2579   if (!sectiondm) sectiondm = dm;
2580   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2581   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2582   if (globalDofSF) PetscAssertPointer(globalDofSF, 5);
2583   if (localDofSF) PetscAssertPointer(localDofSF, 6);
2584   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2585   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2586   if (ishdf5) {
2587 #if defined(PETSC_HAVE_HDF5)
2588     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2589 #else
2590     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2591 #endif
2592   }
2593   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2594   PetscFunctionReturn(PETSC_SUCCESS);
2595 }
2596 
2597 /*@
2598   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2599 
2600   Collective
2601 
2602   Input Parameters:
2603 + dm        - The `DM` that represents the topology
2604 . viewer    - The `PetscViewer` that represents the on-disk vector data
2605 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2606 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2607 - vec       - The global vector to set values of
2608 
2609   Level: advanced
2610 
2611   Notes:
2612   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.
2613 
2614   Calling sequence:
2615 .vb
2616        DMCreate(PETSC_COMM_WORLD, &dm);
2617        DMSetType(dm, DMPLEX);
2618        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2619        DMPlexTopologyLoad(dm, viewer, &sfX);
2620        DMClone(dm, &sectiondm);
2621        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2622        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2623        DMGetGlobalVector(sectiondm, &vec);
2624        PetscObjectSetName((PetscObject)vec, "vec_name");
2625        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2626        DMRestoreGlobalVector(sectiondm, &vec);
2627        PetscSFDestroy(&gsf);
2628        PetscSFDestroy(&sfX);
2629        DMDestroy(&sectiondm);
2630        DMDestroy(&dm);
2631 .ve
2632 
2633 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2634           `PetscSF`, `PetscViewer`
2635 @*/
2636 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2637 {
2638   PetscBool ishdf5;
2639 
2640   PetscFunctionBegin;
2641   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2642   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2643   if (!sectiondm) sectiondm = dm;
2644   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2645   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2646   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2647   /* Check consistency */
2648   {
2649     PetscSection section;
2650     PetscBool    includesConstraints;
2651     PetscInt     m, m1;
2652 
2653     PetscCall(VecGetLocalSize(vec, &m1));
2654     PetscCall(DMGetGlobalSection(sectiondm, &section));
2655     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2656     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2657     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2658     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2659   }
2660   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2661   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2662   if (ishdf5) {
2663 #if defined(PETSC_HAVE_HDF5)
2664     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2665 #else
2666     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2667 #endif
2668   }
2669   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2670   PetscFunctionReturn(PETSC_SUCCESS);
2671 }
2672 
2673 /*@
2674   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2675 
2676   Collective
2677 
2678   Input Parameters:
2679 + dm        - The `DM` that represents the topology
2680 . viewer    - The `PetscViewer` that represents the on-disk vector data
2681 . sectiondm - The `DM` that contains the local section on which vec is defined, can be `NULL`
2682 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2683 - vec       - The local vector to set values of
2684 
2685   Level: advanced
2686 
2687   Notes:
2688   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.
2689 
2690   Calling sequence:
2691 .vb
2692        DMCreate(PETSC_COMM_WORLD, &dm);
2693        DMSetType(dm, DMPLEX);
2694        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2695        DMPlexTopologyLoad(dm, viewer, &sfX);
2696        DMClone(dm, &sectiondm);
2697        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2698        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2699        DMGetLocalVector(sectiondm, &vec);
2700        PetscObjectSetName((PetscObject)vec, "vec_name");
2701        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2702        DMRestoreLocalVector(sectiondm, &vec);
2703        PetscSFDestroy(&lsf);
2704        PetscSFDestroy(&sfX);
2705        DMDestroy(&sectiondm);
2706        DMDestroy(&dm);
2707 .ve
2708 
2709 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2710           `PetscSF`, `PetscViewer`
2711 @*/
2712 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2713 {
2714   PetscBool ishdf5;
2715 
2716   PetscFunctionBegin;
2717   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2718   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2719   if (!sectiondm) sectiondm = dm;
2720   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2721   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2722   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2723   /* Check consistency */
2724   {
2725     PetscSection section;
2726     PetscBool    includesConstraints;
2727     PetscInt     m, m1;
2728 
2729     PetscCall(VecGetLocalSize(vec, &m1));
2730     PetscCall(DMGetLocalSection(sectiondm, &section));
2731     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2732     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2733     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2734     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2735   }
2736   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2737   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2738   if (ishdf5) {
2739 #if defined(PETSC_HAVE_HDF5)
2740     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2741 #else
2742     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2743 #endif
2744   }
2745   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2746   PetscFunctionReturn(PETSC_SUCCESS);
2747 }
2748 
2749 PetscErrorCode DMDestroy_Plex(DM dm)
2750 {
2751   DM_Plex *mesh = (DM_Plex *)dm->data;
2752 
2753   PetscFunctionBegin;
2754   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2755   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2756   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2757   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2758   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", NULL));
2759   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2760   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2761   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2762   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2763   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2764   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2765   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetDefault_C", NULL));
2766   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetDefault_C", NULL));
2767   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetType_C", NULL));
2768   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetType_C", NULL));
2769   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2770   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2771   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetUseCeed_C", NULL));
2772   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetUseCeed_C", NULL));
2773   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2774   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2775   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2776   PetscCall(PetscFree(mesh->cones));
2777   PetscCall(PetscFree(mesh->coneOrientations));
2778   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2779   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2780   PetscCall(PetscFree(mesh->supports));
2781   PetscCall(PetscFree(mesh->cellTypes));
2782   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2783   PetscCall(PetscFree(mesh->tetgenOpts));
2784   PetscCall(PetscFree(mesh->triangleOpts));
2785   PetscCall(PetscFree(mesh->transformType));
2786   PetscCall(PetscFree(mesh->distributionName));
2787   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2788   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2789   PetscCall(ISDestroy(&mesh->subpointIS));
2790   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2791   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2792   if (mesh->periodic.face_sfs) {
2793     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFDestroy(&mesh->periodic.face_sfs[i]));
2794     PetscCall(PetscFree(mesh->periodic.face_sfs));
2795   }
2796   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2797   if (mesh->periodic.periodic_points) {
2798     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(ISDestroy(&mesh->periodic.periodic_points[i]));
2799     PetscCall(PetscFree(mesh->periodic.periodic_points));
2800   }
2801   if (mesh->periodic.transform) PetscCall(PetscFree(mesh->periodic.transform));
2802   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2803   PetscCall(ISDestroy(&mesh->anchorIS));
2804   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2805   PetscCall(PetscFree(mesh->parents));
2806   PetscCall(PetscFree(mesh->childIDs));
2807   PetscCall(PetscSectionDestroy(&mesh->childSection));
2808   PetscCall(PetscFree(mesh->children));
2809   PetscCall(DMDestroy(&mesh->referenceTree));
2810   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2811   PetscCall(PetscFree(mesh->neighbors));
2812   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2813   if (mesh->nonempty_comm != MPI_COMM_NULL && mesh->nonempty_comm != MPI_COMM_SELF) PetscCallMPI(MPI_Comm_free(&mesh->nonempty_comm));
2814   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2815   PetscCall(PetscFree(mesh));
2816   PetscFunctionReturn(PETSC_SUCCESS);
2817 }
2818 
2819 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2820 {
2821   PetscSection           sectionGlobal, sectionLocal;
2822   PetscInt               bs = -1, mbs;
2823   PetscInt               localSize, localStart = 0;
2824   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2825   MatType                mtype;
2826   ISLocalToGlobalMapping ltog;
2827 
2828   PetscFunctionBegin;
2829   PetscCall(MatInitializePackage());
2830   mtype = dm->mattype;
2831   PetscCall(DMGetLocalSection(dm, &sectionLocal));
2832   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2833   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2834   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2835   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2836   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2837   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2838   PetscCall(MatSetType(*J, mtype));
2839   PetscCall(MatSetFromOptions(*J));
2840   PetscCall(MatGetBlockSize(*J, &mbs));
2841   if (mbs > 1) bs = mbs;
2842   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2843   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2844   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2845   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2846   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2847   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2848   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2849   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2850   if (!isShell) {
2851     // There are three states with pblocks, since block starts can have no dofs:
2852     // UNKNOWN) New Block:   An open block has been signalled by pblocks[p] == 1
2853     // TRUE)    Block Start: The first entry in a block has been added
2854     // FALSE)   Block Add:   An additional block entry has been added, since pblocks[p] == 0
2855     PetscBT         blst;
2856     PetscBool3      bstate     = PETSC_BOOL3_UNKNOWN;
2857     PetscBool       fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2858     const PetscInt *perm       = NULL;
2859     PetscInt       *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2860     PetscInt        pStart, pEnd, dof, cdof, num_fields;
2861 
2862     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2863     PetscCall(PetscSectionGetBlockStarts(sectionLocal, &blst));
2864     if (sectionLocal->perm) PetscCall(ISGetIndices(sectionLocal->perm, &perm));
2865 
2866     PetscCall(PetscCalloc1(localSize, &pblocks));
2867     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2868     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2869     // We need to process in the permuted order to get block sizes right
2870     for (PetscInt point = pStart; point < pEnd; ++point) {
2871       const PetscInt p = perm ? perm[point] : point;
2872 
2873       switch (dm->blocking_type) {
2874       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2875         PetscInt bdof, offset;
2876 
2877         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2878         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2879         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2880         if (blst && PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_UNKNOWN;
2881         if (dof > 0) {
2882           // State change
2883           if (bstate == PETSC_BOOL3_UNKNOWN) bstate = PETSC_BOOL3_TRUE;
2884           else if (bstate == PETSC_BOOL3_TRUE && blst && !PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_FALSE;
2885 
2886           for (PetscInt i = 0; i < dof - cdof; ++i) pblocks[offset - localStart + i] = dof - cdof;
2887           // Signal block concatenation
2888           if (bstate == PETSC_BOOL3_FALSE && dof - cdof) pblocks[offset - localStart] = -(dof - cdof);
2889         }
2890         dof  = dof < 0 ? -(dof + 1) : dof;
2891         bdof = cdof && (dof - cdof) ? 1 : dof;
2892         if (dof) {
2893           if (bs < 0) {
2894             bs = bdof;
2895           } else if (bs != bdof) {
2896             bs = 1;
2897           }
2898         }
2899       } break;
2900       case DM_BLOCKING_FIELD_NODE: {
2901         for (PetscInt field = 0; field < num_fields; field++) {
2902           PetscInt num_comp, bdof, offset;
2903           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2904           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2905           if (dof < 0) continue;
2906           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2907           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2908           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);
2909           PetscInt num_nodes = dof / num_comp;
2910           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2911           // Handle possibly constant block size (unlikely)
2912           bdof = cdof && (dof - cdof) ? 1 : dof;
2913           if (dof) {
2914             if (bs < 0) {
2915               bs = bdof;
2916             } else if (bs != bdof) {
2917               bs = 1;
2918             }
2919           }
2920         }
2921       } break;
2922       }
2923     }
2924     if (sectionLocal->perm) PetscCall(ISRestoreIndices(sectionLocal->perm, &perm));
2925     /* Must have same blocksize on all procs (some might have no points) */
2926     bsLocal[0] = bs < 0 ? PETSC_INT_MAX : bs;
2927     bsLocal[1] = bs;
2928     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2929     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2930     else bs = bsMinMax[0];
2931     bs = PetscMax(1, bs);
2932     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2933     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2934       PetscCall(MatSetBlockSize(*J, bs));
2935       PetscCall(MatSetUp(*J));
2936     } else {
2937       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2938       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2939       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2940     }
2941     if (pblocks) { // Consolidate blocks
2942       PetscInt nblocks = 0;
2943       pblocks[0]       = PetscAbs(pblocks[0]);
2944       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2945         if (pblocks[i] == 0) continue;
2946         // Negative block size indicates the blocks should be concatenated
2947         if (pblocks[i] < 0) {
2948           pblocks[i] = -pblocks[i];
2949           pblocks[nblocks - 1] += pblocks[i];
2950         } else {
2951           pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2952         }
2953         for (PetscInt j = 1; j < pblocks[i]; j++)
2954           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);
2955       }
2956       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2957     }
2958     PetscCall(PetscFree(pblocks));
2959   }
2960   PetscCall(MatSetDM(*J, dm));
2961   PetscFunctionReturn(PETSC_SUCCESS);
2962 }
2963 
2964 /*@
2965   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2966 
2967   Not Collective
2968 
2969   Input Parameter:
2970 . dm - The `DMPLEX`
2971 
2972   Output Parameter:
2973 . subsection - The subdomain section
2974 
2975   Level: developer
2976 
2977 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
2978 @*/
2979 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2980 {
2981   DM_Plex *mesh = (DM_Plex *)dm->data;
2982 
2983   PetscFunctionBegin;
2984   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2985   if (!mesh->subdomainSection) {
2986     PetscSection section;
2987     PetscSF      sf;
2988 
2989     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2990     PetscCall(DMGetLocalSection(dm, &section));
2991     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2992     PetscCall(PetscSFDestroy(&sf));
2993   }
2994   *subsection = mesh->subdomainSection;
2995   PetscFunctionReturn(PETSC_SUCCESS);
2996 }
2997 
2998 /*@
2999   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
3000 
3001   Not Collective
3002 
3003   Input Parameter:
3004 . dm - The `DMPLEX`
3005 
3006   Output Parameters:
3007 + pStart - The first mesh point
3008 - pEnd   - The upper bound for mesh points
3009 
3010   Level: beginner
3011 
3012 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
3013 @*/
3014 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
3015 {
3016   DM_Plex *mesh = (DM_Plex *)dm->data;
3017 
3018   PetscFunctionBegin;
3019   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3020   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
3021   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
3022   PetscFunctionReturn(PETSC_SUCCESS);
3023 }
3024 
3025 /*@
3026   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
3027 
3028   Not Collective
3029 
3030   Input Parameters:
3031 + dm     - The `DMPLEX`
3032 . pStart - The first mesh point
3033 - pEnd   - The upper bound for mesh points
3034 
3035   Level: beginner
3036 
3037 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
3038 @*/
3039 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
3040 {
3041   DM_Plex *mesh = (DM_Plex *)dm->data;
3042 
3043   PetscFunctionBegin;
3044   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3045   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
3046   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
3047   PetscCall(PetscFree(mesh->cellTypes));
3048   PetscFunctionReturn(PETSC_SUCCESS);
3049 }
3050 
3051 /*@
3052   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
3053 
3054   Not Collective
3055 
3056   Input Parameters:
3057 + dm - The `DMPLEX`
3058 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3059 
3060   Output Parameter:
3061 . size - The cone size for point `p`
3062 
3063   Level: beginner
3064 
3065 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3066 @*/
3067 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
3068 {
3069   DM_Plex *mesh = (DM_Plex *)dm->data;
3070 
3071   PetscFunctionBegin;
3072   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3073   PetscAssertPointer(size, 3);
3074   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
3075   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
3076   PetscFunctionReturn(PETSC_SUCCESS);
3077 }
3078 
3079 /*@
3080   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
3081 
3082   Not Collective
3083 
3084   Input Parameters:
3085 + dm   - The `DMPLEX`
3086 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3087 - size - The cone size for point `p`
3088 
3089   Level: beginner
3090 
3091   Note:
3092   This should be called after `DMPlexSetChart()`.
3093 
3094 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
3095 @*/
3096 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
3097 {
3098   DM_Plex *mesh = (DM_Plex *)dm->data;
3099 
3100   PetscFunctionBegin;
3101   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3102   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
3103   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
3104   PetscFunctionReturn(PETSC_SUCCESS);
3105 }
3106 
3107 /*@C
3108   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
3109 
3110   Not Collective
3111 
3112   Input Parameters:
3113 + dm - The `DMPLEX`
3114 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3115 
3116   Output Parameter:
3117 . cone - An array of points which are on the in-edges for point `p`, the length of `cone` is the result of `DMPlexGetConeSize()`
3118 
3119   Level: beginner
3120 
3121   Fortran Notes:
3122   `cone` must be declared with
3123 .vb
3124   PetscInt, pointer :: cone(:)
3125 .ve
3126 
3127   You must also call `DMPlexRestoreCone()` after you finish using the array.
3128   `DMPlexRestoreCone()` is not needed/available in C.
3129 
3130 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
3131 @*/
3132 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
3133 {
3134   DM_Plex *mesh = (DM_Plex *)dm->data;
3135   PetscInt off;
3136 
3137   PetscFunctionBegin;
3138   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3139   PetscAssertPointer(cone, 3);
3140   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3141   *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3142   PetscFunctionReturn(PETSC_SUCCESS);
3143 }
3144 
3145 /*@
3146   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
3147 
3148   Not Collective
3149 
3150   Input Parameters:
3151 + dm - The `DMPLEX`
3152 - p  - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3153 
3154   Output Parameters:
3155 + pConesSection - `PetscSection` describing the layout of `pCones`
3156 - pCones        - An `IS` containing the points which are on the in-edges for the point set `p`
3157 
3158   Level: intermediate
3159 
3160 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
3161 @*/
3162 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
3163 {
3164   PetscSection cs, newcs;
3165   PetscInt    *cones;
3166   PetscInt    *newarr = NULL;
3167   PetscInt     n;
3168 
3169   PetscFunctionBegin;
3170   PetscCall(DMPlexGetCones(dm, &cones));
3171   PetscCall(DMPlexGetConeSection(dm, &cs));
3172   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
3173   if (pConesSection) *pConesSection = newcs;
3174   if (pCones) {
3175     PetscCall(PetscSectionGetStorageSize(newcs, &n));
3176     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
3177   }
3178   PetscFunctionReturn(PETSC_SUCCESS);
3179 }
3180 
3181 /*@
3182   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
3183 
3184   Not Collective
3185 
3186   Input Parameters:
3187 + dm     - The `DMPLEX`
3188 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3189 
3190   Output Parameter:
3191 . expandedPoints - An `IS` containing the of vertices recursively expanded from input points
3192 
3193   Level: advanced
3194 
3195   Notes:
3196   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
3197 
3198   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
3199 
3200 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
3201           `DMPlexGetDepth()`, `IS`
3202 @*/
3203 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
3204 {
3205   IS      *expandedPointsAll;
3206   PetscInt depth;
3207 
3208   PetscFunctionBegin;
3209   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3210   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3211   PetscAssertPointer(expandedPoints, 3);
3212   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3213   *expandedPoints = expandedPointsAll[0];
3214   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
3215   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3216   PetscFunctionReturn(PETSC_SUCCESS);
3217 }
3218 
3219 /*@
3220   DMPlexGetConeRecursive - Expand each given point into its cone points and do that recursively until we end up just with vertices
3221   (DAG points of depth 0, i.e., without cones).
3222 
3223   Not Collective
3224 
3225   Input Parameters:
3226 + dm     - The `DMPLEX`
3227 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3228 
3229   Output Parameters:
3230 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3231 . expandedPoints - (optional) An array of index sets with recursively expanded cones
3232 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3233 
3234   Level: advanced
3235 
3236   Notes:
3237   Like `DMPlexGetConeTuple()` but recursive.
3238 
3239   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.
3240   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
3241 
3242   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\:
3243   (1) DAG points in `expandedPoints`[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
3244   (2) DAG points in `expandedPoints`[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
3245 
3246 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3247           `DMPlexGetDepth()`, `PetscSection`, `IS`
3248 @*/
3249 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3250 {
3251   const PetscInt *arr0 = NULL, *cone = NULL;
3252   PetscInt       *arr = NULL, *newarr = NULL;
3253   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
3254   IS             *expandedPoints_;
3255   PetscSection   *sections_;
3256 
3257   PetscFunctionBegin;
3258   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3259   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3260   if (depth) PetscAssertPointer(depth, 3);
3261   if (expandedPoints) PetscAssertPointer(expandedPoints, 4);
3262   if (sections) PetscAssertPointer(sections, 5);
3263   PetscCall(ISGetLocalSize(points, &n));
3264   PetscCall(ISGetIndices(points, &arr0));
3265   PetscCall(DMPlexGetDepth(dm, &depth_));
3266   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
3267   PetscCall(PetscCalloc1(depth_, &sections_));
3268   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
3269   for (d = depth_ - 1; d >= 0; d--) {
3270     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
3271     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
3272     for (i = 0; i < n; i++) {
3273       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
3274       if (arr[i] >= start && arr[i] < end) {
3275         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
3276         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
3277       } else {
3278         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
3279       }
3280     }
3281     PetscCall(PetscSectionSetUp(sections_[d]));
3282     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
3283     PetscCall(PetscMalloc1(newn, &newarr));
3284     for (i = 0; i < n; i++) {
3285       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
3286       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
3287       if (cn > 1) {
3288         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
3289         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3290       } else {
3291         newarr[co] = arr[i];
3292       }
3293     }
3294     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3295     arr = newarr;
3296     n   = newn;
3297   }
3298   PetscCall(ISRestoreIndices(points, &arr0));
3299   *depth = depth_;
3300   if (expandedPoints) *expandedPoints = expandedPoints_;
3301   else {
3302     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3303     PetscCall(PetscFree(expandedPoints_));
3304   }
3305   if (sections) *sections = sections_;
3306   else {
3307     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3308     PetscCall(PetscFree(sections_));
3309   }
3310   PetscFunctionReturn(PETSC_SUCCESS);
3311 }
3312 
3313 /*@
3314   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3315 
3316   Not Collective
3317 
3318   Input Parameters:
3319 + dm     - The `DMPLEX`
3320 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3321 
3322   Output Parameters:
3323 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3324 . expandedPoints - (optional) An array of recursively expanded cones
3325 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3326 
3327   Level: advanced
3328 
3329   Note:
3330   See `DMPlexGetConeRecursive()`
3331 
3332 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3333           `DMPlexGetDepth()`, `IS`, `PetscSection`
3334 @*/
3335 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3336 {
3337   PetscInt d, depth_;
3338 
3339   PetscFunctionBegin;
3340   PetscCall(DMPlexGetDepth(dm, &depth_));
3341   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3342   if (depth) *depth = 0;
3343   if (expandedPoints) {
3344     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&(*expandedPoints)[d]));
3345     PetscCall(PetscFree(*expandedPoints));
3346   }
3347   if (sections) {
3348     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&(*sections)[d]));
3349     PetscCall(PetscFree(*sections));
3350   }
3351   PetscFunctionReturn(PETSC_SUCCESS);
3352 }
3353 
3354 /*@
3355   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
3356 
3357   Not Collective
3358 
3359   Input Parameters:
3360 + dm   - The `DMPLEX`
3361 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3362 - cone - An array of points which are on the in-edges for point `p`, its length must have been previously provided with `DMPlexSetConeSize()`
3363 
3364   Level: beginner
3365 
3366   Note:
3367   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3368 
3369 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3370 @*/
3371 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3372 {
3373   DM_Plex *mesh = (DM_Plex *)dm->data;
3374   PetscInt dof, off, c;
3375 
3376   PetscFunctionBegin;
3377   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3378   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3379   if (dof) PetscAssertPointer(cone, 3);
3380   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3381   if (PetscDefined(USE_DEBUG)) {
3382     PetscInt pStart, pEnd;
3383     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3384     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);
3385     for (c = 0; c < dof; ++c) {
3386       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);
3387       mesh->cones[off + c] = cone[c];
3388     }
3389   } else {
3390     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3391   }
3392   PetscFunctionReturn(PETSC_SUCCESS);
3393 }
3394 
3395 /*@C
3396   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3397 
3398   Not Collective
3399 
3400   Input Parameters:
3401 + dm - The `DMPLEX`
3402 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3403 
3404   Output Parameter:
3405 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3406                     integer giving the prescription for cone traversal. Its length is given by the result of `DMPlexSetConeSize()`
3407 
3408   Level: beginner
3409 
3410   Note:
3411   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3412   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3413   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3414   with the identity.
3415 
3416   Fortran Notes:
3417   You must call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3418   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3419 
3420 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetConeSize()`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`,
3421           `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3422 @*/
3423 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3424 {
3425   DM_Plex *mesh = (DM_Plex *)dm->data;
3426   PetscInt off;
3427 
3428   PetscFunctionBegin;
3429   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3430   if (PetscDefined(USE_DEBUG)) {
3431     PetscInt dof;
3432     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3433     if (dof) PetscAssertPointer(coneOrientation, 3);
3434   }
3435   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3436 
3437   *coneOrientation = &mesh->coneOrientations[off];
3438   PetscFunctionReturn(PETSC_SUCCESS);
3439 }
3440 
3441 /*@
3442   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3443 
3444   Not Collective
3445 
3446   Input Parameters:
3447 + dm              - The `DMPLEX`
3448 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3449 - coneOrientation - An array of orientations. Its length is given by the result of `DMPlexSetConeSize()`
3450 
3451   Level: beginner
3452 
3453   Notes:
3454   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3455 
3456   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3457 
3458 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3459 @*/
3460 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3461 {
3462   DM_Plex *mesh = (DM_Plex *)dm->data;
3463   PetscInt pStart, pEnd;
3464   PetscInt dof, off, c;
3465 
3466   PetscFunctionBegin;
3467   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3468   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3469   if (dof) PetscAssertPointer(coneOrientation, 3);
3470   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3471   if (PetscDefined(USE_DEBUG)) {
3472     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3473     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);
3474     for (c = 0; c < dof; ++c) {
3475       PetscInt cdof, o = coneOrientation[c];
3476 
3477       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3478       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);
3479       mesh->coneOrientations[off + c] = o;
3480     }
3481   } else {
3482     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3483   }
3484   PetscFunctionReturn(PETSC_SUCCESS);
3485 }
3486 
3487 /*@
3488   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3489 
3490   Not Collective
3491 
3492   Input Parameters:
3493 + dm        - The `DMPLEX`
3494 . p         - The point, which must lie in the chart set with `DMPlexSetChart()`
3495 . conePos   - The local index in the cone where the point should be put
3496 - conePoint - The mesh point to insert
3497 
3498   Level: beginner
3499 
3500 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3501 @*/
3502 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3503 {
3504   DM_Plex *mesh = (DM_Plex *)dm->data;
3505   PetscInt pStart, pEnd;
3506   PetscInt dof, off;
3507 
3508   PetscFunctionBegin;
3509   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3510   if (PetscDefined(USE_DEBUG)) {
3511     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3512     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);
3513     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);
3514     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3515     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);
3516   }
3517   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3518   mesh->cones[off + conePos] = conePoint;
3519   PetscFunctionReturn(PETSC_SUCCESS);
3520 }
3521 
3522 /*@
3523   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3524 
3525   Not Collective
3526 
3527   Input Parameters:
3528 + dm              - The `DMPLEX`
3529 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3530 . conePos         - The local index in the cone where the point should be put
3531 - coneOrientation - The point orientation to insert
3532 
3533   Level: beginner
3534 
3535   Note:
3536   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3537 
3538 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3539 @*/
3540 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3541 {
3542   DM_Plex *mesh = (DM_Plex *)dm->data;
3543   PetscInt pStart, pEnd;
3544   PetscInt dof, off;
3545 
3546   PetscFunctionBegin;
3547   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3548   if (PetscDefined(USE_DEBUG)) {
3549     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3550     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);
3551     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3552     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);
3553   }
3554   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3555   mesh->coneOrientations[off + conePos] = coneOrientation;
3556   PetscFunctionReturn(PETSC_SUCCESS);
3557 }
3558 
3559 /*@C
3560   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3561 
3562   Not collective
3563 
3564   Input Parameters:
3565 + dm - The DMPlex
3566 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3567 
3568   Output Parameters:
3569 + cone - An array of points which are on the in-edges for point `p`
3570 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3571          integer giving the prescription for cone traversal.
3572 
3573   Level: beginner
3574 
3575   Notes:
3576   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3577   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3578   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3579   with the identity.
3580 
3581   You must also call `DMPlexRestoreOrientedCone()` after you finish using the returned array.
3582 
3583   Fortran Notes:
3584   `cone` and `ornt` must be declared with
3585 .vb
3586   PetscInt, pointer :: cone(:)
3587   PetscInt, pointer :: ornt(:)
3588 .ve
3589 
3590 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3591 @*/
3592 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3593 {
3594   DM_Plex *mesh = (DM_Plex *)dm->data;
3595 
3596   PetscFunctionBegin;
3597   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3598   if (mesh->tr) {
3599     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3600   } else {
3601     PetscInt off;
3602     if (PetscDefined(USE_DEBUG)) {
3603       PetscInt dof;
3604       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3605       if (dof) {
3606         if (cone) PetscAssertPointer(cone, 3);
3607         if (ornt) PetscAssertPointer(ornt, 4);
3608       }
3609     }
3610     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3611     if (cone) *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3612     if (ornt) *ornt = PetscSafePointerPlusOffset(mesh->coneOrientations, off);
3613   }
3614   PetscFunctionReturn(PETSC_SUCCESS);
3615 }
3616 
3617 /*@C
3618   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG obtained with `DMPlexGetOrientedCone()`
3619 
3620   Not Collective
3621 
3622   Input Parameters:
3623 + dm   - The DMPlex
3624 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3625 . cone - An array of points which are on the in-edges for point p
3626 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3627          integer giving the prescription for cone traversal.
3628 
3629   Level: beginner
3630 
3631 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3632 @*/
3633 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3634 {
3635   DM_Plex *mesh = (DM_Plex *)dm->data;
3636 
3637   PetscFunctionBegin;
3638   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3639   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3640   PetscFunctionReturn(PETSC_SUCCESS);
3641 }
3642 
3643 /*@
3644   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3645 
3646   Not Collective
3647 
3648   Input Parameters:
3649 + dm - The `DMPLEX`
3650 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3651 
3652   Output Parameter:
3653 . size - The support size for point `p`
3654 
3655   Level: beginner
3656 
3657 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3658 @*/
3659 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3660 {
3661   DM_Plex *mesh = (DM_Plex *)dm->data;
3662 
3663   PetscFunctionBegin;
3664   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3665   PetscAssertPointer(size, 3);
3666   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3667   PetscFunctionReturn(PETSC_SUCCESS);
3668 }
3669 
3670 /*@
3671   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3672 
3673   Not Collective
3674 
3675   Input Parameters:
3676 + dm   - The `DMPLEX`
3677 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3678 - size - The support size for point `p`
3679 
3680   Level: beginner
3681 
3682   Note:
3683   This should be called after `DMPlexSetChart()`.
3684 
3685 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3686 @*/
3687 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3688 {
3689   DM_Plex *mesh = (DM_Plex *)dm->data;
3690 
3691   PetscFunctionBegin;
3692   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3693   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3694   PetscFunctionReturn(PETSC_SUCCESS);
3695 }
3696 
3697 /*@C
3698   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3699 
3700   Not Collective
3701 
3702   Input Parameters:
3703 + dm - The `DMPLEX`
3704 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3705 
3706   Output Parameter:
3707 . support - An array of points which are on the out-edges for point `p`, its length is that obtained from `DMPlexGetSupportSize()`
3708 
3709   Level: beginner
3710 
3711   Fortran Notes:
3712   `support` must be declared with
3713 .vb
3714   PetscInt, pointer :: support(:)
3715 .ve
3716 
3717   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3718   `DMPlexRestoreSupport()` is not needed/available in C.
3719 
3720 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3721 @*/
3722 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3723 {
3724   DM_Plex *mesh = (DM_Plex *)dm->data;
3725   PetscInt off;
3726 
3727   PetscFunctionBegin;
3728   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3729   PetscAssertPointer(support, 3);
3730   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3731   *support = PetscSafePointerPlusOffset(mesh->supports, off);
3732   PetscFunctionReturn(PETSC_SUCCESS);
3733 }
3734 
3735 /*@
3736   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3737 
3738   Not Collective
3739 
3740   Input Parameters:
3741 + dm      - The `DMPLEX`
3742 . p       - The point, which must lie in the chart set with `DMPlexSetChart()`
3743 - support - An array of points which are on the out-edges for point `p`, its length is that obtained from `DMPlexGetSupportSize()`
3744 
3745   Level: beginner
3746 
3747   Note:
3748   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3749 
3750 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3751 @*/
3752 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3753 {
3754   DM_Plex *mesh = (DM_Plex *)dm->data;
3755   PetscInt pStart, pEnd;
3756   PetscInt dof, off, c;
3757 
3758   PetscFunctionBegin;
3759   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3760   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3761   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3762   if (dof) PetscAssertPointer(support, 3);
3763   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3764   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);
3765   for (c = 0; c < dof; ++c) {
3766     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);
3767     mesh->supports[off + c] = support[c];
3768   }
3769   PetscFunctionReturn(PETSC_SUCCESS);
3770 }
3771 
3772 /*@
3773   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3774 
3775   Not Collective
3776 
3777   Input Parameters:
3778 + dm           - The `DMPLEX`
3779 . p            - The point, which must lie in the chart set with `DMPlexSetChart()`
3780 . supportPos   - The local index in the cone where the point should be put
3781 - supportPoint - The mesh point to insert
3782 
3783   Level: beginner
3784 
3785 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3786 @*/
3787 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3788 {
3789   DM_Plex *mesh = (DM_Plex *)dm->data;
3790   PetscInt pStart, pEnd;
3791   PetscInt dof, off;
3792 
3793   PetscFunctionBegin;
3794   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3795   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3796   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3797   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3798   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);
3799   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);
3800   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);
3801   mesh->supports[off + supportPos] = supportPoint;
3802   PetscFunctionReturn(PETSC_SUCCESS);
3803 }
3804 
3805 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3806 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3807 {
3808   switch (ct) {
3809   case DM_POLYTOPE_SEGMENT:
3810     if (o == -1) return -2;
3811     break;
3812   case DM_POLYTOPE_TRIANGLE:
3813     if (o == -3) return -1;
3814     if (o == -2) return -3;
3815     if (o == -1) return -2;
3816     break;
3817   case DM_POLYTOPE_QUADRILATERAL:
3818     if (o == -4) return -2;
3819     if (o == -3) return -1;
3820     if (o == -2) return -4;
3821     if (o == -1) return -3;
3822     break;
3823   default:
3824     return o;
3825   }
3826   return o;
3827 }
3828 
3829 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3830 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3831 {
3832   switch (ct) {
3833   case DM_POLYTOPE_SEGMENT:
3834     if ((o == -2) || (o == 1)) return -1;
3835     if (o == -1) return 0;
3836     break;
3837   case DM_POLYTOPE_TRIANGLE:
3838     if (o == -3) return -2;
3839     if (o == -2) return -1;
3840     if (o == -1) return -3;
3841     break;
3842   case DM_POLYTOPE_QUADRILATERAL:
3843     if (o == -4) return -2;
3844     if (o == -3) return -1;
3845     if (o == -2) return -4;
3846     if (o == -1) return -3;
3847     break;
3848   default:
3849     return o;
3850   }
3851   return o;
3852 }
3853 
3854 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3855 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3856 {
3857   PetscInt pStart, pEnd, p;
3858 
3859   PetscFunctionBegin;
3860   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3861   for (p = pStart; p < pEnd; ++p) {
3862     const PetscInt *cone, *ornt;
3863     PetscInt        coneSize, c;
3864 
3865     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3866     PetscCall(DMPlexGetCone(dm, p, &cone));
3867     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3868     for (c = 0; c < coneSize; ++c) {
3869       DMPolytopeType ct;
3870       const PetscInt o = ornt[c];
3871 
3872       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3873       switch (ct) {
3874       case DM_POLYTOPE_SEGMENT:
3875         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3876         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3877         break;
3878       case DM_POLYTOPE_TRIANGLE:
3879         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3880         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3881         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3882         break;
3883       case DM_POLYTOPE_QUADRILATERAL:
3884         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3885         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3886         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3887         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3888         break;
3889       default:
3890         break;
3891       }
3892     }
3893   }
3894   PetscFunctionReturn(PETSC_SUCCESS);
3895 }
3896 
3897 static inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3898 {
3899   DM_Plex *mesh = (DM_Plex *)dm->data;
3900 
3901   PetscFunctionBeginHot;
3902   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3903     if (useCone) {
3904       PetscCall(DMPlexGetConeSize(dm, p, size));
3905       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3906     } else {
3907       PetscCall(DMPlexGetSupportSize(dm, p, size));
3908       PetscCall(DMPlexGetSupport(dm, p, arr));
3909     }
3910   } else {
3911     if (useCone) {
3912       const PetscSection s   = mesh->coneSection;
3913       const PetscInt     ps  = p - s->pStart;
3914       const PetscInt     off = s->atlasOff[ps];
3915 
3916       *size = s->atlasDof[ps];
3917       *arr  = mesh->cones + off;
3918       *ornt = mesh->coneOrientations + off;
3919     } else {
3920       const PetscSection s   = mesh->supportSection;
3921       const PetscInt     ps  = p - s->pStart;
3922       const PetscInt     off = s->atlasOff[ps];
3923 
3924       *size = s->atlasDof[ps];
3925       *arr  = mesh->supports + off;
3926     }
3927   }
3928   PetscFunctionReturn(PETSC_SUCCESS);
3929 }
3930 
3931 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3932 {
3933   DM_Plex *mesh = (DM_Plex *)dm->data;
3934 
3935   PetscFunctionBeginHot;
3936   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3937     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
3938   }
3939   PetscFunctionReturn(PETSC_SUCCESS);
3940 }
3941 
3942 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3943 {
3944   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3945   PetscInt       *closure;
3946   const PetscInt *tmp = NULL, *tmpO = NULL;
3947   PetscInt        off = 0, tmpSize, t;
3948 
3949   PetscFunctionBeginHot;
3950   if (ornt) {
3951     PetscCall(DMPlexGetCellType(dm, p, &ct));
3952     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;
3953   }
3954   if (*points) {
3955     closure = *points;
3956   } else {
3957     PetscInt maxConeSize, maxSupportSize;
3958     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3959     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3960   }
3961   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3962   if (ct == DM_POLYTOPE_UNKNOWN) {
3963     closure[off++] = p;
3964     closure[off++] = 0;
3965     for (t = 0; t < tmpSize; ++t) {
3966       closure[off++] = tmp[t];
3967       closure[off++] = tmpO ? tmpO[t] : 0;
3968     }
3969   } else {
3970     const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, ornt);
3971 
3972     /* We assume that cells with a valid type have faces with a valid type */
3973     closure[off++] = p;
3974     closure[off++] = ornt;
3975     for (t = 0; t < tmpSize; ++t) {
3976       DMPolytopeType ft;
3977 
3978       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3979       closure[off++] = tmp[arr[t]];
3980       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3981     }
3982   }
3983   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3984   if (numPoints) *numPoints = tmpSize + 1;
3985   if (points) *points = closure;
3986   PetscFunctionReturn(PETSC_SUCCESS);
3987 }
3988 
3989 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3990 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3991 {
3992   const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, o);
3993   const PetscInt *cone, *ornt;
3994   PetscInt       *pts, *closure = NULL;
3995   DMPolytopeType  ft;
3996   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3997   PetscInt        dim, coneSize, c, d, clSize, cl;
3998 
3999   PetscFunctionBeginHot;
4000   PetscCall(DMGetDimension(dm, &dim));
4001   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
4002   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
4003   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
4004   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
4005   maxSize       = PetscMax(coneSeries, supportSeries);
4006   if (*points) {
4007     pts = *points;
4008   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
4009   c        = 0;
4010   pts[c++] = point;
4011   pts[c++] = o;
4012   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
4013   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
4014   for (cl = 0; cl < clSize * 2; cl += 2) {
4015     pts[c++] = closure[cl];
4016     pts[c++] = closure[cl + 1];
4017   }
4018   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
4019   for (cl = 0; cl < clSize * 2; cl += 2) {
4020     pts[c++] = closure[cl];
4021     pts[c++] = closure[cl + 1];
4022   }
4023   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
4024   for (d = 2; d < coneSize; ++d) {
4025     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
4026     pts[c++] = cone[arr[d * 2 + 0]];
4027     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
4028   }
4029   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
4030   if (dim >= 3) {
4031     for (d = 2; d < coneSize; ++d) {
4032       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
4033       const PetscInt *fcone, *fornt;
4034       PetscInt        fconeSize, fc, i;
4035 
4036       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
4037       const PetscInt *farr = DMPolytopeTypeGetArrangement(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
4038       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
4039       for (fc = 0; fc < fconeSize; ++fc) {
4040         const PetscInt cp = fcone[farr[fc * 2 + 0]];
4041         const PetscInt co = farr[fc * 2 + 1];
4042 
4043         for (i = 0; i < c; i += 2)
4044           if (pts[i] == cp) break;
4045         if (i == c) {
4046           PetscCall(DMPlexGetCellType(dm, cp, &ft));
4047           pts[c++] = cp;
4048           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
4049         }
4050       }
4051       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
4052     }
4053   }
4054   *numPoints = c / 2;
4055   *points    = pts;
4056   PetscFunctionReturn(PETSC_SUCCESS);
4057 }
4058 
4059 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4060 {
4061   DMPolytopeType ct;
4062   PetscInt      *closure, *fifo;
4063   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
4064   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
4065   PetscInt       depth, maxSize;
4066 
4067   PetscFunctionBeginHot;
4068   PetscCall(DMPlexGetDepth(dm, &depth));
4069   if (depth == 1) {
4070     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
4071     PetscFunctionReturn(PETSC_SUCCESS);
4072   }
4073   PetscCall(DMPlexGetCellType(dm, p, &ct));
4074   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;
4075   if (DMPolytopeTypeIsHybrid(ct) && ct != DM_POLYTOPE_POINT_PRISM_TENSOR) {
4076     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
4077     PetscFunctionReturn(PETSC_SUCCESS);
4078   }
4079   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
4080   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
4081   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
4082   maxSize       = PetscMax(coneSeries, supportSeries);
4083   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4084   if (*points) {
4085     closure = *points;
4086   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
4087   closure[closureSize++] = p;
4088   closure[closureSize++] = ornt;
4089   fifo[fifoSize++]       = p;
4090   fifo[fifoSize++]       = ornt;
4091   fifo[fifoSize++]       = ct;
4092   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
4093   while (fifoSize - fifoStart) {
4094     const PetscInt       q    = fifo[fifoStart++];
4095     const PetscInt       o    = fifo[fifoStart++];
4096     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
4097     const PetscInt      *qarr = DMPolytopeTypeGetArrangement(qt, o);
4098     const PetscInt      *tmp, *tmpO = NULL;
4099     PetscInt             tmpSize, t;
4100 
4101     if (PetscDefined(USE_DEBUG)) {
4102       PetscInt nO = DMPolytopeTypeGetNumArrangements(qt) / 2;
4103       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);
4104     }
4105     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4106     for (t = 0; t < tmpSize; ++t) {
4107       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
4108       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
4109       const PetscInt cp = tmp[ip];
4110       PetscCall(DMPlexGetCellType(dm, cp, &ct));
4111       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
4112       PetscInt       c;
4113 
4114       /* Check for duplicate */
4115       for (c = 0; c < closureSize; c += 2) {
4116         if (closure[c] == cp) break;
4117       }
4118       if (c == closureSize) {
4119         closure[closureSize++] = cp;
4120         closure[closureSize++] = co;
4121         fifo[fifoSize++]       = cp;
4122         fifo[fifoSize++]       = co;
4123         fifo[fifoSize++]       = ct;
4124       }
4125     }
4126     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4127   }
4128   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4129   if (numPoints) *numPoints = closureSize / 2;
4130   if (points) *points = closure;
4131   PetscFunctionReturn(PETSC_SUCCESS);
4132 }
4133 
4134 /*@C
4135   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
4136 
4137   Not Collective
4138 
4139   Input Parameters:
4140 + dm      - The `DMPLEX`
4141 . p       - The mesh point
4142 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
4143 
4144   Input/Output Parameter:
4145 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
4146            if *points is `NULL` on input, internal storage will be returned, use `DMPlexRestoreTransitiveClosure()`,
4147            otherwise the provided array is used to hold the values
4148 
4149   Output Parameter:
4150 . numPoints - The number of points in the closure, so `points` is of size 2*`numPoints`
4151 
4152   Level: beginner
4153 
4154   Note:
4155   If using internal storage (points is `NULL` on input), each call overwrites the last output.
4156 
4157   Fortran Notes:
4158   `points` must be declared with
4159 .vb
4160   PetscInt, pointer :: points(:)
4161 .ve
4162   and is always allocated by the function.
4163 
4164   The `numPoints` argument is not present in the Fortran binding.
4165 
4166 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4167 @*/
4168 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4169 {
4170   PetscFunctionBeginHot;
4171   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4172   if (numPoints) PetscAssertPointer(numPoints, 4);
4173   if (points) PetscAssertPointer(points, 5);
4174   if (PetscDefined(USE_DEBUG)) {
4175     PetscInt pStart, pEnd;
4176     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4177     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);
4178   }
4179   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
4180   PetscFunctionReturn(PETSC_SUCCESS);
4181 }
4182 
4183 /*@C
4184   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
4185 
4186   Not Collective
4187 
4188   Input Parameters:
4189 + dm        - The `DMPLEX`
4190 . p         - The mesh point
4191 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
4192 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4193 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
4194 
4195   Level: beginner
4196 
4197   Note:
4198   If not using internal storage (points is not `NULL` on input), this call is unnecessary
4199 
4200 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4201 @*/
4202 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4203 {
4204   PetscFunctionBeginHot;
4205   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4206   if (numPoints) *numPoints = 0;
4207   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
4208   PetscFunctionReturn(PETSC_SUCCESS);
4209 }
4210 
4211 /*@
4212   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
4213 
4214   Not Collective
4215 
4216   Input Parameter:
4217 . dm - The `DMPLEX`
4218 
4219   Output Parameters:
4220 + maxConeSize    - The maximum number of in-edges
4221 - maxSupportSize - The maximum number of out-edges
4222 
4223   Level: beginner
4224 
4225 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
4226 @*/
4227 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
4228 {
4229   DM_Plex *mesh = (DM_Plex *)dm->data;
4230 
4231   PetscFunctionBegin;
4232   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4233   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
4234   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
4235   PetscFunctionReturn(PETSC_SUCCESS);
4236 }
4237 
4238 PetscErrorCode DMSetUp_Plex(DM dm)
4239 {
4240   DM_Plex *mesh = (DM_Plex *)dm->data;
4241   PetscInt size, maxSupportSize;
4242 
4243   PetscFunctionBegin;
4244   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4245   PetscCall(PetscSectionSetUp(mesh->coneSection));
4246   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
4247   PetscCall(PetscMalloc1(size, &mesh->cones));
4248   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
4249   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4250   if (maxSupportSize) {
4251     PetscCall(PetscSectionSetUp(mesh->supportSection));
4252     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
4253     PetscCall(PetscMalloc1(size, &mesh->supports));
4254   }
4255   PetscFunctionReturn(PETSC_SUCCESS);
4256 }
4257 
4258 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
4259 {
4260   PetscFunctionBegin;
4261   if (subdm) PetscCall(DMClone(dm, subdm));
4262   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, NULL, NULL, is, subdm));
4263   if (subdm) (*subdm)->useNatural = dm->useNatural;
4264   if (dm->useNatural && dm->sfMigration) {
4265     PetscSF sfNatural;
4266 
4267     (*subdm)->sfMigration = dm->sfMigration;
4268     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
4269     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
4270     (*subdm)->sfNatural = sfNatural;
4271   }
4272   PetscFunctionReturn(PETSC_SUCCESS);
4273 }
4274 
4275 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
4276 {
4277   PetscInt i = 0;
4278 
4279   PetscFunctionBegin;
4280   PetscCall(DMClone(dms[0], superdm));
4281   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
4282   (*superdm)->useNatural = PETSC_FALSE;
4283   for (i = 0; i < len; i++) {
4284     if (dms[i]->useNatural && dms[i]->sfMigration) {
4285       PetscSF sfNatural;
4286 
4287       (*superdm)->sfMigration = dms[i]->sfMigration;
4288       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
4289       (*superdm)->useNatural = PETSC_TRUE;
4290       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
4291       (*superdm)->sfNatural = sfNatural;
4292       break;
4293     }
4294   }
4295   PetscFunctionReturn(PETSC_SUCCESS);
4296 }
4297 
4298 /*@
4299   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
4300 
4301   Not Collective
4302 
4303   Input Parameter:
4304 . dm - The `DMPLEX`
4305 
4306   Level: beginner
4307 
4308   Note:
4309   This should be called after all calls to `DMPlexSetCone()`
4310 
4311 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4312 @*/
4313 PetscErrorCode DMPlexSymmetrize(DM dm)
4314 {
4315   DM_Plex  *mesh = (DM_Plex *)dm->data;
4316   PetscInt *offsets;
4317   PetscInt  supportSize;
4318   PetscInt  pStart, pEnd, p;
4319 
4320   PetscFunctionBegin;
4321   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4322   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4323   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4324   /* Calculate support sizes */
4325   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4326   for (p = pStart; p < pEnd; ++p) {
4327     PetscInt dof, off, c;
4328 
4329     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4330     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4331     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4332   }
4333   PetscCall(PetscSectionSetUp(mesh->supportSection));
4334   /* Calculate supports */
4335   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4336   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4337   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4338   for (p = pStart; p < pEnd; ++p) {
4339     PetscInt dof, off, c;
4340 
4341     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4342     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4343     for (c = off; c < off + dof; ++c) {
4344       const PetscInt q = mesh->cones[c];
4345       PetscInt       offS;
4346 
4347       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4348 
4349       mesh->supports[offS + offsets[q]] = p;
4350       ++offsets[q];
4351     }
4352   }
4353   PetscCall(PetscFree(offsets));
4354   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4355   PetscFunctionReturn(PETSC_SUCCESS);
4356 }
4357 
4358 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4359 {
4360   IS stratumIS;
4361 
4362   PetscFunctionBegin;
4363   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4364   if (PetscDefined(USE_DEBUG)) {
4365     PetscInt  qStart, qEnd, numLevels, level;
4366     PetscBool overlap = PETSC_FALSE;
4367     PetscCall(DMLabelGetNumValues(label, &numLevels));
4368     for (level = 0; level < numLevels; level++) {
4369       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4370       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4371         overlap = PETSC_TRUE;
4372         break;
4373       }
4374     }
4375     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);
4376   }
4377   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4378   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4379   PetscCall(ISDestroy(&stratumIS));
4380   PetscFunctionReturn(PETSC_SUCCESS);
4381 }
4382 
4383 static PetscErrorCode DMPlexStratify_CellType_Private(DM dm, DMLabel label)
4384 {
4385   PetscInt *pMin, *pMax;
4386   PetscInt  pStart, pEnd;
4387   PetscInt  dmin = PETSC_INT_MAX, dmax = PETSC_INT_MIN;
4388 
4389   PetscFunctionBegin;
4390   {
4391     DMLabel label2;
4392 
4393     PetscCall(DMPlexGetCellTypeLabel(dm, &label2));
4394     PetscCall(PetscObjectViewFromOptions((PetscObject)label2, NULL, "-ct_view"));
4395   }
4396   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4397   for (PetscInt p = pStart; p < pEnd; ++p) {
4398     DMPolytopeType ct;
4399 
4400     PetscCall(DMPlexGetCellType(dm, p, &ct));
4401     dmin = PetscMin(DMPolytopeTypeGetDim(ct), dmin);
4402     dmax = PetscMax(DMPolytopeTypeGetDim(ct), dmax);
4403   }
4404   PetscCall(PetscMalloc2(dmax + 1, &pMin, dmax + 1, &pMax));
4405   for (PetscInt d = dmin; d <= dmax; ++d) {
4406     pMin[d] = PETSC_INT_MAX;
4407     pMax[d] = PETSC_INT_MIN;
4408   }
4409   for (PetscInt p = pStart; p < pEnd; ++p) {
4410     DMPolytopeType ct;
4411     PetscInt       d;
4412 
4413     PetscCall(DMPlexGetCellType(dm, p, &ct));
4414     d       = DMPolytopeTypeGetDim(ct);
4415     pMin[d] = PetscMin(p, pMin[d]);
4416     pMax[d] = PetscMax(p, pMax[d]);
4417   }
4418   for (PetscInt d = dmin; d <= dmax; ++d) {
4419     if (pMin[d] > pMax[d]) continue;
4420     PetscCall(DMPlexCreateDepthStratum(dm, label, d, pMin[d], pMax[d] + 1));
4421   }
4422   PetscCall(PetscFree2(pMin, pMax));
4423   PetscFunctionReturn(PETSC_SUCCESS);
4424 }
4425 
4426 static PetscErrorCode DMPlexStratify_Topological_Private(DM dm, DMLabel label)
4427 {
4428   PetscInt pStart, pEnd;
4429   PetscInt numRoots = 0, numLeaves = 0;
4430 
4431   PetscFunctionBegin;
4432   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4433   {
4434     /* Initialize roots and count leaves */
4435     PetscInt sMin = PETSC_INT_MAX;
4436     PetscInt sMax = PETSC_INT_MIN;
4437     PetscInt coneSize, supportSize;
4438 
4439     for (PetscInt p = pStart; p < pEnd; ++p) {
4440       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4441       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4442       if (!coneSize && supportSize) {
4443         sMin = PetscMin(p, sMin);
4444         sMax = PetscMax(p, sMax);
4445         ++numRoots;
4446       } else if (!supportSize && coneSize) {
4447         ++numLeaves;
4448       } else if (!supportSize && !coneSize) {
4449         /* Isolated points */
4450         sMin = PetscMin(p, sMin);
4451         sMax = PetscMax(p, sMax);
4452       }
4453     }
4454     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4455   }
4456 
4457   if (numRoots + numLeaves == (pEnd - pStart)) {
4458     PetscInt sMin = PETSC_INT_MAX;
4459     PetscInt sMax = PETSC_INT_MIN;
4460     PetscInt coneSize, supportSize;
4461 
4462     for (PetscInt p = pStart; p < pEnd; ++p) {
4463       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4464       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4465       if (!supportSize && coneSize) {
4466         sMin = PetscMin(p, sMin);
4467         sMax = PetscMax(p, sMax);
4468       }
4469     }
4470     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4471   } else {
4472     PetscInt level = 0;
4473     PetscInt qStart, qEnd;
4474 
4475     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4476     while (qEnd > qStart) {
4477       PetscInt sMin = PETSC_INT_MAX;
4478       PetscInt sMax = PETSC_INT_MIN;
4479 
4480       for (PetscInt q = qStart; q < qEnd; ++q) {
4481         const PetscInt *support;
4482         PetscInt        supportSize;
4483 
4484         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4485         PetscCall(DMPlexGetSupport(dm, q, &support));
4486         for (PetscInt s = 0; s < supportSize; ++s) {
4487           sMin = PetscMin(support[s], sMin);
4488           sMax = PetscMax(support[s], sMax);
4489         }
4490       }
4491       PetscCall(DMLabelGetNumValues(label, &level));
4492       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4493       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4494     }
4495   }
4496   PetscFunctionReturn(PETSC_SUCCESS);
4497 }
4498 
4499 /*@
4500   DMPlexStratify - Computes the strata for all points in the `DMPLEX`
4501 
4502   Collective
4503 
4504   Input Parameter:
4505 . dm - The `DMPLEX`
4506 
4507   Level: beginner
4508 
4509   Notes:
4510   The strata group all points of the same grade, and this function calculates the strata. This
4511   grade can be seen as the height (or depth) of the point in the DAG.
4512 
4513   The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4514   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram).
4515   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4516   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4517   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4518   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4519   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4520 
4521   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4522   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4523   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
4524   to interpolate only that one (e0), so that
4525 .vb
4526   cone(c0) = {e0, v2}
4527   cone(e0) = {v0, v1}
4528 .ve
4529   If `DMPlexStratify()` is run on this mesh, it will give depths
4530 .vb
4531    depth 0 = {v0, v1, v2}
4532    depth 1 = {e0, c0}
4533 .ve
4534   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4535 
4536   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4537 
4538 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4539 @*/
4540 PetscErrorCode DMPlexStratify(DM dm)
4541 {
4542   DM_Plex  *mesh = (DM_Plex *)dm->data;
4543   DMLabel   label;
4544   PetscBool flg = PETSC_FALSE;
4545 
4546   PetscFunctionBegin;
4547   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4548   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4549 
4550   // Create depth label
4551   PetscCall(DMRemoveLabel(dm, "depth", NULL));
4552   PetscCall(DMCreateLabel(dm, "depth"));
4553   PetscCall(DMPlexGetDepthLabel(dm, &label));
4554 
4555   PetscCall(PetscOptionsGetBool(NULL, dm->hdr.prefix, "-dm_plex_stratify_celltype", &flg, NULL));
4556   if (flg) PetscCall(DMPlexStratify_CellType_Private(dm, label));
4557   else PetscCall(DMPlexStratify_Topological_Private(dm, label));
4558 
4559   { /* just in case there is an empty process */
4560     PetscInt numValues, maxValues = 0, v;
4561 
4562     PetscCall(DMLabelGetNumValues(label, &numValues));
4563     PetscCallMPI(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4564     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4565   }
4566   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4567   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4568   PetscFunctionReturn(PETSC_SUCCESS);
4569 }
4570 
4571 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4572 {
4573   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4574   PetscInt       dim, depth, pheight, coneSize;
4575 
4576   PetscFunctionBeginHot;
4577   PetscCall(DMGetDimension(dm, &dim));
4578   PetscCall(DMPlexGetDepth(dm, &depth));
4579   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4580   pheight = depth - pdepth;
4581   if (depth <= 1) {
4582     switch (pdepth) {
4583     case 0:
4584       ct = DM_POLYTOPE_POINT;
4585       break;
4586     case 1:
4587       switch (coneSize) {
4588       case 2:
4589         ct = DM_POLYTOPE_SEGMENT;
4590         break;
4591       case 3:
4592         ct = DM_POLYTOPE_TRIANGLE;
4593         break;
4594       case 4:
4595         switch (dim) {
4596         case 2:
4597           ct = DM_POLYTOPE_QUADRILATERAL;
4598           break;
4599         case 3:
4600           ct = DM_POLYTOPE_TETRAHEDRON;
4601           break;
4602         default:
4603           break;
4604         }
4605         break;
4606       case 5:
4607         ct = DM_POLYTOPE_PYRAMID;
4608         break;
4609       case 6:
4610         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4611         break;
4612       case 8:
4613         ct = DM_POLYTOPE_HEXAHEDRON;
4614         break;
4615       default:
4616         break;
4617       }
4618     }
4619   } else {
4620     if (pdepth == 0) {
4621       ct = DM_POLYTOPE_POINT;
4622     } else if (pheight == 0) {
4623       switch (dim) {
4624       case 1:
4625         switch (coneSize) {
4626         case 2:
4627           ct = DM_POLYTOPE_SEGMENT;
4628           break;
4629         default:
4630           break;
4631         }
4632         break;
4633       case 2:
4634         switch (coneSize) {
4635         case 3:
4636           ct = DM_POLYTOPE_TRIANGLE;
4637           break;
4638         case 4:
4639           ct = DM_POLYTOPE_QUADRILATERAL;
4640           break;
4641         default:
4642           break;
4643         }
4644         break;
4645       case 3:
4646         switch (coneSize) {
4647         case 4:
4648           ct = DM_POLYTOPE_TETRAHEDRON;
4649           break;
4650         case 5: {
4651           const PetscInt *cone;
4652           PetscInt        faceConeSize;
4653 
4654           PetscCall(DMPlexGetCone(dm, p, &cone));
4655           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4656           switch (faceConeSize) {
4657           case 3:
4658             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4659             break;
4660           case 4:
4661             ct = DM_POLYTOPE_PYRAMID;
4662             break;
4663           }
4664         } break;
4665         case 6:
4666           ct = DM_POLYTOPE_HEXAHEDRON;
4667           break;
4668         default:
4669           break;
4670         }
4671         break;
4672       default:
4673         break;
4674       }
4675     } else if (pheight > 0) {
4676       switch (coneSize) {
4677       case 2:
4678         ct = DM_POLYTOPE_SEGMENT;
4679         break;
4680       case 3:
4681         ct = DM_POLYTOPE_TRIANGLE;
4682         break;
4683       case 4:
4684         ct = DM_POLYTOPE_QUADRILATERAL;
4685         break;
4686       default:
4687         break;
4688       }
4689     }
4690   }
4691   *pt = ct;
4692   PetscFunctionReturn(PETSC_SUCCESS);
4693 }
4694 
4695 /*@
4696   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4697 
4698   Collective
4699 
4700   Input Parameter:
4701 . dm - The `DMPLEX`
4702 
4703   Level: developer
4704 
4705   Note:
4706   This function is normally called automatically when a cell type is requested. It creates an
4707   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4708   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4709 
4710   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4711 
4712 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4713 @*/
4714 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4715 {
4716   DM_Plex *mesh;
4717   DMLabel  ctLabel;
4718   PetscInt pStart, pEnd, p;
4719 
4720   PetscFunctionBegin;
4721   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4722   mesh = (DM_Plex *)dm->data;
4723   PetscCall(DMCreateLabel(dm, "celltype"));
4724   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4725   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4726   PetscCall(PetscFree(mesh->cellTypes));
4727   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4728   for (p = pStart; p < pEnd; ++p) {
4729     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4730     PetscInt       pdepth;
4731 
4732     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4733     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4734     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]);
4735     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4736     mesh->cellTypes[p - pStart].value_as_uint8 = (uint8_t)ct;
4737   }
4738   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4739   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4740   PetscFunctionReturn(PETSC_SUCCESS);
4741 }
4742 
4743 /*@C
4744   DMPlexGetJoin - Get an array for the join of the set of points
4745 
4746   Not Collective
4747 
4748   Input Parameters:
4749 + dm        - The `DMPLEX` object
4750 . numPoints - The number of input points for the join
4751 - points    - The input points
4752 
4753   Output Parameters:
4754 + numCoveredPoints - The number of points in the join
4755 - coveredPoints    - The points in the join
4756 
4757   Level: intermediate
4758 
4759   Note:
4760   Currently, this is restricted to a single level join
4761 
4762   Fortran Notes:
4763   `converedPoints` must be declared with
4764 .vb
4765   PetscInt, pointer :: coveredPints(:)
4766 .ve
4767 
4768   The `numCoveredPoints` argument is not present in the Fortran binding.
4769 
4770 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4771 @*/
4772 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4773 {
4774   DM_Plex  *mesh = (DM_Plex *)dm->data;
4775   PetscInt *join[2];
4776   PetscInt  joinSize, i = 0;
4777   PetscInt  dof, off, p, c, m;
4778   PetscInt  maxSupportSize;
4779 
4780   PetscFunctionBegin;
4781   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4782   PetscAssertPointer(points, 3);
4783   PetscAssertPointer(numCoveredPoints, 4);
4784   PetscAssertPointer(coveredPoints, 5);
4785   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4786   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4787   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4788   /* Copy in support of first point */
4789   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4790   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4791   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4792   /* Check each successive support */
4793   for (p = 1; p < numPoints; ++p) {
4794     PetscInt newJoinSize = 0;
4795 
4796     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4797     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4798     for (c = 0; c < dof; ++c) {
4799       const PetscInt point = mesh->supports[off + c];
4800 
4801       for (m = 0; m < joinSize; ++m) {
4802         if (point == join[i][m]) {
4803           join[1 - i][newJoinSize++] = point;
4804           break;
4805         }
4806       }
4807     }
4808     joinSize = newJoinSize;
4809     i        = 1 - i;
4810   }
4811   *numCoveredPoints = joinSize;
4812   *coveredPoints    = join[i];
4813   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4814   PetscFunctionReturn(PETSC_SUCCESS);
4815 }
4816 
4817 /*@C
4818   DMPlexRestoreJoin - Restore an array for the join of the set of points obtained with `DMPlexGetJoin()`
4819 
4820   Not Collective
4821 
4822   Input Parameters:
4823 + dm        - The `DMPLEX` object
4824 . numPoints - The number of input points for the join
4825 - points    - The input points
4826 
4827   Output Parameters:
4828 + numCoveredPoints - The number of points in the join
4829 - coveredPoints    - The points in the join
4830 
4831   Level: intermediate
4832 
4833   Fortran Notes:
4834   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4835 
4836 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4837 @*/
4838 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4839 {
4840   PetscFunctionBegin;
4841   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4842   if (points) PetscAssertPointer(points, 3);
4843   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4844   PetscAssertPointer(coveredPoints, 5);
4845   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4846   if (numCoveredPoints) *numCoveredPoints = 0;
4847   PetscFunctionReturn(PETSC_SUCCESS);
4848 }
4849 
4850 /*@C
4851   DMPlexGetFullJoin - Get an array for the join of the set of points
4852 
4853   Not Collective
4854 
4855   Input Parameters:
4856 + dm        - The `DMPLEX` object
4857 . numPoints - The number of input points for the join
4858 - points    - The input points, its length is `numPoints`
4859 
4860   Output Parameters:
4861 + numCoveredPoints - The number of points in the join
4862 - coveredPoints    - The points in the join, its length is `numCoveredPoints`
4863 
4864   Level: intermediate
4865 
4866   Fortran Notes:
4867   `points` and `converedPoints` must be declared with
4868 .vb
4869   PetscInt, pointer :: points(:)
4870   PetscInt, pointer :: coveredPints(:)
4871 .ve
4872 
4873   The `numCoveredPoints` argument is not present in the Fortran binding.
4874 
4875 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4876 @*/
4877 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4878 {
4879   PetscInt *offsets, **closures;
4880   PetscInt *join[2];
4881   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4882   PetscInt  p, d, c, m, ms;
4883 
4884   PetscFunctionBegin;
4885   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4886   PetscAssertPointer(points, 3);
4887   PetscAssertPointer(numCoveredPoints, 4);
4888   PetscAssertPointer(coveredPoints, 5);
4889 
4890   PetscCall(DMPlexGetDepth(dm, &depth));
4891   PetscCall(PetscCalloc1(numPoints, &closures));
4892   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4893   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4894   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4895   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4896   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4897 
4898   for (p = 0; p < numPoints; ++p) {
4899     PetscInt closureSize;
4900 
4901     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4902 
4903     offsets[p * (depth + 2) + 0] = 0;
4904     for (d = 0; d < depth + 1; ++d) {
4905       PetscInt pStart, pEnd, i;
4906 
4907       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4908       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4909         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4910           offsets[p * (depth + 2) + d + 1] = i;
4911           break;
4912         }
4913       }
4914       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4915     }
4916     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);
4917   }
4918   for (d = 0; d < depth + 1; ++d) {
4919     PetscInt dof;
4920 
4921     /* Copy in support of first point */
4922     dof = offsets[d + 1] - offsets[d];
4923     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4924     /* Check each successive cone */
4925     for (p = 1; p < numPoints && joinSize; ++p) {
4926       PetscInt newJoinSize = 0;
4927 
4928       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4929       for (c = 0; c < dof; ++c) {
4930         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4931 
4932         for (m = 0; m < joinSize; ++m) {
4933           if (point == join[i][m]) {
4934             join[1 - i][newJoinSize++] = point;
4935             break;
4936           }
4937         }
4938       }
4939       joinSize = newJoinSize;
4940       i        = 1 - i;
4941     }
4942     if (joinSize) break;
4943   }
4944   *numCoveredPoints = joinSize;
4945   *coveredPoints    = join[i];
4946   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4947   PetscCall(PetscFree(closures));
4948   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4949   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4950   PetscFunctionReturn(PETSC_SUCCESS);
4951 }
4952 
4953 /*@C
4954   DMPlexGetMeet - Get an array for the meet of the set of points
4955 
4956   Not Collective
4957 
4958   Input Parameters:
4959 + dm        - The `DMPLEX` object
4960 . numPoints - The number of input points for the meet
4961 - points    - The input points, of length `numPoints`
4962 
4963   Output Parameters:
4964 + numCoveringPoints - The number of points in the meet
4965 - coveringPoints    - The points in the meet, of length `numCoveringPoints`
4966 
4967   Level: intermediate
4968 
4969   Note:
4970   Currently, this is restricted to a single level meet
4971 
4972   Fortran Notes:
4973   `coveringPoints` must be declared with
4974 .vb
4975   PetscInt, pointer :: coveringPoints(:)
4976 .ve
4977 
4978   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4979 
4980 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4981 @*/
4982 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt *coveringPoints[])
4983 {
4984   DM_Plex  *mesh = (DM_Plex *)dm->data;
4985   PetscInt *meet[2];
4986   PetscInt  meetSize, i = 0;
4987   PetscInt  dof, off, p, c, m;
4988   PetscInt  maxConeSize;
4989 
4990   PetscFunctionBegin;
4991   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4992   PetscAssertPointer(points, 3);
4993   PetscAssertPointer(numCoveringPoints, 4);
4994   PetscAssertPointer(coveringPoints, 5);
4995   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4996   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4997   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4998   /* Copy in cone of first point */
4999   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
5000   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
5001   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
5002   /* Check each successive cone */
5003   for (p = 1; p < numPoints; ++p) {
5004     PetscInt newMeetSize = 0;
5005 
5006     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
5007     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
5008     for (c = 0; c < dof; ++c) {
5009       const PetscInt point = mesh->cones[off + c];
5010 
5011       for (m = 0; m < meetSize; ++m) {
5012         if (point == meet[i][m]) {
5013           meet[1 - i][newMeetSize++] = point;
5014           break;
5015         }
5016       }
5017     }
5018     meetSize = newMeetSize;
5019     i        = 1 - i;
5020   }
5021   *numCoveringPoints = meetSize;
5022   *coveringPoints    = meet[i];
5023   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
5024   PetscFunctionReturn(PETSC_SUCCESS);
5025 }
5026 
5027 /*@C
5028   DMPlexRestoreMeet - Restore an array for the meet of the set of points obtained with `DMPlexGetMeet()`
5029 
5030   Not Collective
5031 
5032   Input Parameters:
5033 + dm        - The `DMPLEX` object
5034 . numPoints - The number of input points for the meet
5035 - points    - The input points
5036 
5037   Output Parameters:
5038 + numCoveredPoints - The number of points in the meet
5039 - coveredPoints    - The points in the meet
5040 
5041   Level: intermediate
5042 
5043   Fortran Notes:
5044   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
5045 
5046 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
5047 @*/
5048 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
5049 {
5050   PetscFunctionBegin;
5051   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5052   if (points) PetscAssertPointer(points, 3);
5053   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
5054   PetscAssertPointer(coveredPoints, 5);
5055   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
5056   if (numCoveredPoints) *numCoveredPoints = 0;
5057   PetscFunctionReturn(PETSC_SUCCESS);
5058 }
5059 
5060 /*@C
5061   DMPlexGetFullMeet - Get an array for the meet of the set of points
5062 
5063   Not Collective
5064 
5065   Input Parameters:
5066 + dm        - The `DMPLEX` object
5067 . numPoints - The number of input points for the meet
5068 - points    - The input points, of length  `numPoints`
5069 
5070   Output Parameters:
5071 + numCoveredPoints - The number of points in the meet
5072 - coveredPoints    - The points in the meet, of length  `numCoveredPoints`
5073 
5074   Level: intermediate
5075 
5076   Fortran Notes:
5077   `points` and `coveredPoints` must be declared with
5078 .vb
5079   PetscInt, pointer :: points(:)
5080   PetscInt, pointer :: coveredPoints(:)
5081 .ve
5082 
5083   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
5084 
5085 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
5086 @*/
5087 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
5088 {
5089   PetscInt *offsets, **closures;
5090   PetscInt *meet[2];
5091   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
5092   PetscInt  p, h, c, m, mc;
5093 
5094   PetscFunctionBegin;
5095   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5096   PetscAssertPointer(points, 3);
5097   PetscAssertPointer(numCoveredPoints, 4);
5098   PetscAssertPointer(coveredPoints, 5);
5099 
5100   PetscCall(DMPlexGetDepth(dm, &height));
5101   PetscCall(PetscMalloc1(numPoints, &closures));
5102   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5103   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
5104   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
5105   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
5106   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
5107 
5108   for (p = 0; p < numPoints; ++p) {
5109     PetscInt closureSize;
5110 
5111     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
5112 
5113     offsets[p * (height + 2) + 0] = 0;
5114     for (h = 0; h < height + 1; ++h) {
5115       PetscInt pStart, pEnd, i;
5116 
5117       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
5118       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
5119         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
5120           offsets[p * (height + 2) + h + 1] = i;
5121           break;
5122         }
5123       }
5124       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
5125     }
5126     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);
5127   }
5128   for (h = 0; h < height + 1; ++h) {
5129     PetscInt dof;
5130 
5131     /* Copy in cone of first point */
5132     dof = offsets[h + 1] - offsets[h];
5133     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
5134     /* Check each successive cone */
5135     for (p = 1; p < numPoints && meetSize; ++p) {
5136       PetscInt newMeetSize = 0;
5137 
5138       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
5139       for (c = 0; c < dof; ++c) {
5140         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
5141 
5142         for (m = 0; m < meetSize; ++m) {
5143           if (point == meet[i][m]) {
5144             meet[1 - i][newMeetSize++] = point;
5145             break;
5146           }
5147         }
5148       }
5149       meetSize = newMeetSize;
5150       i        = 1 - i;
5151     }
5152     if (meetSize) break;
5153   }
5154   *numCoveredPoints = meetSize;
5155   *coveredPoints    = meet[i];
5156   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
5157   PetscCall(PetscFree(closures));
5158   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5159   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
5160   PetscFunctionReturn(PETSC_SUCCESS);
5161 }
5162 
5163 /*@
5164   DMPlexEqual - Determine if two `DM` have the same topology
5165 
5166   Not Collective
5167 
5168   Input Parameters:
5169 + dmA - A `DMPLEX` object
5170 - dmB - A `DMPLEX` object
5171 
5172   Output Parameter:
5173 . equal - `PETSC_TRUE` if the topologies are identical
5174 
5175   Level: intermediate
5176 
5177   Note:
5178   We are not solving graph isomorphism, so we do not permute.
5179 
5180 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5181 @*/
5182 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
5183 {
5184   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
5185 
5186   PetscFunctionBegin;
5187   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
5188   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
5189   PetscAssertPointer(equal, 3);
5190 
5191   *equal = PETSC_FALSE;
5192   PetscCall(DMPlexGetDepth(dmA, &depth));
5193   PetscCall(DMPlexGetDepth(dmB, &depthB));
5194   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
5195   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
5196   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
5197   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
5198   for (p = pStart; p < pEnd; ++p) {
5199     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
5200     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
5201 
5202     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
5203     PetscCall(DMPlexGetCone(dmA, p, &cone));
5204     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
5205     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
5206     PetscCall(DMPlexGetCone(dmB, p, &coneB));
5207     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
5208     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5209     for (c = 0; c < coneSize; ++c) {
5210       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5211       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5212     }
5213     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
5214     PetscCall(DMPlexGetSupport(dmA, p, &support));
5215     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
5216     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
5217     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5218     for (s = 0; s < supportSize; ++s) {
5219       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
5220     }
5221   }
5222   *equal = PETSC_TRUE;
5223   PetscFunctionReturn(PETSC_SUCCESS);
5224 }
5225 
5226 /*@
5227   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
5228 
5229   Not Collective
5230 
5231   Input Parameters:
5232 + dm         - The `DMPLEX`
5233 . cellDim    - The cell dimension
5234 - numCorners - The number of vertices on a cell
5235 
5236   Output Parameter:
5237 . numFaceVertices - The number of vertices on a face
5238 
5239   Level: developer
5240 
5241   Note:
5242   Of course this can only work for a restricted set of symmetric shapes
5243 
5244 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5245 @*/
5246 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
5247 {
5248   MPI_Comm comm;
5249 
5250   PetscFunctionBegin;
5251   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
5252   PetscAssertPointer(numFaceVertices, 4);
5253   switch (cellDim) {
5254   case 0:
5255     *numFaceVertices = 0;
5256     break;
5257   case 1:
5258     *numFaceVertices = 1;
5259     break;
5260   case 2:
5261     switch (numCorners) {
5262     case 3:                 /* triangle */
5263       *numFaceVertices = 2; /* Edge has 2 vertices */
5264       break;
5265     case 4:                 /* quadrilateral */
5266       *numFaceVertices = 2; /* Edge has 2 vertices */
5267       break;
5268     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
5269       *numFaceVertices = 3; /* Edge has 3 vertices */
5270       break;
5271     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
5272       *numFaceVertices = 3; /* Edge has 3 vertices */
5273       break;
5274     default:
5275       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5276     }
5277     break;
5278   case 3:
5279     switch (numCorners) {
5280     case 4:                 /* tetradehdron */
5281       *numFaceVertices = 3; /* Face has 3 vertices */
5282       break;
5283     case 6:                 /* tet cohesive cells */
5284       *numFaceVertices = 4; /* Face has 4 vertices */
5285       break;
5286     case 8:                 /* hexahedron */
5287       *numFaceVertices = 4; /* Face has 4 vertices */
5288       break;
5289     case 9:                 /* tet cohesive Lagrange cells */
5290       *numFaceVertices = 6; /* Face has 6 vertices */
5291       break;
5292     case 10:                /* quadratic tetrahedron */
5293       *numFaceVertices = 6; /* Face has 6 vertices */
5294       break;
5295     case 12:                /* hex cohesive Lagrange cells */
5296       *numFaceVertices = 6; /* Face has 6 vertices */
5297       break;
5298     case 18:                /* quadratic tet cohesive Lagrange cells */
5299       *numFaceVertices = 6; /* Face has 6 vertices */
5300       break;
5301     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
5302       *numFaceVertices = 9; /* Face has 9 vertices */
5303       break;
5304     default:
5305       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5306     }
5307     break;
5308   default:
5309     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
5310   }
5311   PetscFunctionReturn(PETSC_SUCCESS);
5312 }
5313 
5314 /*@
5315   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
5316 
5317   Not Collective
5318 
5319   Input Parameter:
5320 . dm - The `DMPLEX` object
5321 
5322   Output Parameter:
5323 . depthLabel - The `DMLabel` recording point depth
5324 
5325   Level: developer
5326 
5327 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
5328 @*/
5329 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
5330 {
5331   PetscFunctionBegin;
5332   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5333   PetscAssertPointer(depthLabel, 2);
5334   *depthLabel = dm->depthLabel;
5335   PetscFunctionReturn(PETSC_SUCCESS);
5336 }
5337 
5338 /*@
5339   DMPlexGetDepth - Get the depth of the DAG representing this mesh
5340 
5341   Not Collective
5342 
5343   Input Parameter:
5344 . dm - The `DMPLEX` object
5345 
5346   Output Parameter:
5347 . depth - The number of strata (breadth first levels) in the DAG
5348 
5349   Level: developer
5350 
5351   Notes:
5352   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
5353 
5354   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
5355 
5356   An empty mesh gives -1.
5357 
5358 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
5359 @*/
5360 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
5361 {
5362   DM_Plex *mesh = (DM_Plex *)dm->data;
5363   DMLabel  label;
5364   PetscInt d = -1;
5365 
5366   PetscFunctionBegin;
5367   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5368   PetscAssertPointer(depth, 2);
5369   if (mesh->tr) {
5370     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
5371   } else {
5372     PetscCall(DMPlexGetDepthLabel(dm, &label));
5373     // Allow missing depths
5374     if (label) PetscCall(DMLabelGetValueBounds(label, NULL, &d));
5375     *depth = d;
5376   }
5377   PetscFunctionReturn(PETSC_SUCCESS);
5378 }
5379 
5380 /*@
5381   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
5382 
5383   Not Collective
5384 
5385   Input Parameters:
5386 + dm    - The `DMPLEX` object
5387 - depth - The requested depth
5388 
5389   Output Parameters:
5390 + start - The first point at this `depth`
5391 - end   - One beyond the last point at this `depth`
5392 
5393   Level: developer
5394 
5395   Notes:
5396   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5397   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5398   higher dimension, e.g., "edges".
5399 
5400 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5401 @*/
5402 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
5403 {
5404   DM_Plex *mesh = (DM_Plex *)dm->data;
5405   DMLabel  label;
5406   PetscInt pStart, pEnd;
5407 
5408   PetscFunctionBegin;
5409   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5410   if (start) {
5411     PetscAssertPointer(start, 3);
5412     *start = 0;
5413   }
5414   if (end) {
5415     PetscAssertPointer(end, 4);
5416     *end = 0;
5417   }
5418   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5419   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5420   if (depth < 0) {
5421     if (start) *start = pStart;
5422     if (end) *end = pEnd;
5423     PetscFunctionReturn(PETSC_SUCCESS);
5424   }
5425   if (mesh->tr) {
5426     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5427   } else {
5428     PetscCall(DMPlexGetDepthLabel(dm, &label));
5429     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5430     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5431   }
5432   PetscFunctionReturn(PETSC_SUCCESS);
5433 }
5434 
5435 /*@
5436   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5437 
5438   Not Collective
5439 
5440   Input Parameters:
5441 + dm     - The `DMPLEX` object
5442 - height - The requested height
5443 
5444   Output Parameters:
5445 + start - The first point at this `height`
5446 - end   - One beyond the last point at this `height`
5447 
5448   Level: developer
5449 
5450   Notes:
5451   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5452   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5453   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5454 
5455 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5456 @*/
5457 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5458 {
5459   DMLabel  label;
5460   PetscInt depth, pStart, pEnd;
5461 
5462   PetscFunctionBegin;
5463   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5464   if (start) {
5465     PetscAssertPointer(start, 3);
5466     *start = 0;
5467   }
5468   if (end) {
5469     PetscAssertPointer(end, 4);
5470     *end = 0;
5471   }
5472   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5473   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5474   if (height < 0) {
5475     if (start) *start = pStart;
5476     if (end) *end = pEnd;
5477     PetscFunctionReturn(PETSC_SUCCESS);
5478   }
5479   PetscCall(DMPlexGetDepthLabel(dm, &label));
5480   if (label) PetscCall(DMLabelGetNumValues(label, &depth));
5481   else PetscCall(DMGetDimension(dm, &depth));
5482   PetscCheck(depth >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Depth not yet computed");
5483   PetscCall(DMPlexGetDepthStratum(dm, depth - 1 - height, start, end));
5484   PetscFunctionReturn(PETSC_SUCCESS);
5485 }
5486 
5487 /*@
5488   DMPlexGetPointDepth - Get the `depth` of a given point
5489 
5490   Not Collective
5491 
5492   Input Parameters:
5493 + dm    - The `DMPLEX` object
5494 - point - The point
5495 
5496   Output Parameter:
5497 . depth - The depth of the `point`
5498 
5499   Level: intermediate
5500 
5501 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5502 @*/
5503 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5504 {
5505   PetscFunctionBegin;
5506   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5507   PetscAssertPointer(depth, 3);
5508   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5509   PetscFunctionReturn(PETSC_SUCCESS);
5510 }
5511 
5512 /*@
5513   DMPlexGetPointHeight - Get the `height` of a given point
5514 
5515   Not Collective
5516 
5517   Input Parameters:
5518 + dm    - The `DMPLEX` object
5519 - point - The point
5520 
5521   Output Parameter:
5522 . height - The height of the `point`
5523 
5524   Level: intermediate
5525 
5526 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5527 @*/
5528 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5529 {
5530   PetscInt n, pDepth;
5531 
5532   PetscFunctionBegin;
5533   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5534   PetscAssertPointer(height, 3);
5535   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5536   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5537   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5538   PetscFunctionReturn(PETSC_SUCCESS);
5539 }
5540 
5541 /*@
5542   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5543 
5544   Not Collective
5545 
5546   Input Parameter:
5547 . dm - The `DMPLEX` object
5548 
5549   Output Parameter:
5550 . celltypeLabel - The `DMLabel` recording cell polytope type
5551 
5552   Level: developer
5553 
5554   Note:
5555   This function will trigger automatica computation of cell types. This can be disabled by calling
5556   `DMCreateLabel`(dm, "celltype") beforehand.
5557 
5558 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5559 @*/
5560 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5561 {
5562   PetscFunctionBegin;
5563   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5564   PetscAssertPointer(celltypeLabel, 2);
5565   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5566   *celltypeLabel = dm->celltypeLabel;
5567   PetscFunctionReturn(PETSC_SUCCESS);
5568 }
5569 
5570 /*@
5571   DMPlexGetCellType - Get the polytope type of a given cell
5572 
5573   Not Collective
5574 
5575   Input Parameters:
5576 + dm   - The `DMPLEX` object
5577 - cell - The cell
5578 
5579   Output Parameter:
5580 . celltype - The polytope type of the cell
5581 
5582   Level: intermediate
5583 
5584 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5585 @*/
5586 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5587 {
5588   DM_Plex *mesh = (DM_Plex *)dm->data;
5589   DMLabel  label;
5590   PetscInt ct;
5591 
5592   PetscFunctionBegin;
5593   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5594   PetscAssertPointer(celltype, 3);
5595   if (mesh->tr) {
5596     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5597   } else {
5598     PetscInt pStart, pEnd;
5599 
5600     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5601     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5602       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5603       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5604       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5605       for (PetscInt p = pStart; p < pEnd; p++) {
5606         PetscCall(DMLabelGetValue(label, p, &ct));
5607         mesh->cellTypes[p - pStart].value_as_uint8 = (uint8_t)ct;
5608       }
5609     }
5610     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5611     if (PetscDefined(USE_DEBUG)) {
5612       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5613       PetscCall(DMLabelGetValue(label, cell, &ct));
5614       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5615       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5616     }
5617   }
5618   PetscFunctionReturn(PETSC_SUCCESS);
5619 }
5620 
5621 /*@
5622   DMPlexSetCellType - Set the polytope type of a given cell
5623 
5624   Not Collective
5625 
5626   Input Parameters:
5627 + dm       - The `DMPLEX` object
5628 . cell     - The cell
5629 - celltype - The polytope type of the cell
5630 
5631   Level: advanced
5632 
5633   Note:
5634   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5635   is executed. This function will override the computed type. However, if automatic classification will not succeed
5636   and a user wants to manually specify all types, the classification must be disabled by calling
5637   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5638 
5639 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5640 @*/
5641 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5642 {
5643   DM_Plex *mesh = (DM_Plex *)dm->data;
5644   DMLabel  label;
5645   PetscInt pStart, pEnd;
5646 
5647   PetscFunctionBegin;
5648   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5649   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5650   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5651   PetscCall(DMLabelSetValue(label, cell, celltype));
5652   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5653   mesh->cellTypes[cell - pStart].value_as_uint8 = (uint8_t)celltype;
5654   PetscFunctionReturn(PETSC_SUCCESS);
5655 }
5656 
5657 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5658 {
5659   PetscSection section;
5660   PetscInt     maxHeight;
5661   const char  *prefix;
5662 
5663   PetscFunctionBegin;
5664   PetscCall(DMClone(dm, cdm));
5665   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5666   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5667   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5668   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5669   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5670   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5671   PetscCall(DMSetLocalSection(*cdm, section));
5672   PetscCall(PetscSectionDestroy(&section));
5673 
5674   PetscCall(DMSetNumFields(*cdm, 1));
5675   PetscCall(DMCreateDS(*cdm));
5676   (*cdm)->cloneOpts = PETSC_TRUE;
5677   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5678   PetscFunctionReturn(PETSC_SUCCESS);
5679 }
5680 
5681 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5682 {
5683   Vec coordsLocal, cellCoordsLocal;
5684   DM  coordsDM, cellCoordsDM;
5685 
5686   PetscFunctionBegin;
5687   *field = NULL;
5688   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5689   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5690   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5691   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5692   if (coordsLocal && coordsDM) {
5693     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5694     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5695   }
5696   PetscFunctionReturn(PETSC_SUCCESS);
5697 }
5698 
5699 /*@
5700   DMPlexGetConeSection - Return a section which describes the layout of cone data
5701 
5702   Not Collective
5703 
5704   Input Parameter:
5705 . dm - The `DMPLEX` object
5706 
5707   Output Parameter:
5708 . section - The `PetscSection` object
5709 
5710   Level: developer
5711 
5712 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5713 @*/
5714 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5715 {
5716   DM_Plex *mesh = (DM_Plex *)dm->data;
5717 
5718   PetscFunctionBegin;
5719   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5720   if (section) *section = mesh->coneSection;
5721   PetscFunctionReturn(PETSC_SUCCESS);
5722 }
5723 
5724 /*@
5725   DMPlexGetSupportSection - Return a section which describes the layout of support data
5726 
5727   Not Collective
5728 
5729   Input Parameter:
5730 . dm - The `DMPLEX` object
5731 
5732   Output Parameter:
5733 . section - The `PetscSection` object
5734 
5735   Level: developer
5736 
5737 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5738 @*/
5739 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5740 {
5741   DM_Plex *mesh = (DM_Plex *)dm->data;
5742 
5743   PetscFunctionBegin;
5744   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5745   if (section) *section = mesh->supportSection;
5746   PetscFunctionReturn(PETSC_SUCCESS);
5747 }
5748 
5749 /*@C
5750   DMPlexGetCones - Return cone data
5751 
5752   Not Collective
5753 
5754   Input Parameter:
5755 . dm - The `DMPLEX` object
5756 
5757   Output Parameter:
5758 . cones - The cone for each point
5759 
5760   Level: developer
5761 
5762 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5763 @*/
5764 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5765 {
5766   DM_Plex *mesh = (DM_Plex *)dm->data;
5767 
5768   PetscFunctionBegin;
5769   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5770   if (cones) *cones = mesh->cones;
5771   PetscFunctionReturn(PETSC_SUCCESS);
5772 }
5773 
5774 /*@C
5775   DMPlexGetConeOrientations - Return cone orientation data
5776 
5777   Not Collective
5778 
5779   Input Parameter:
5780 . dm - The `DMPLEX` object
5781 
5782   Output Parameter:
5783 . coneOrientations - The array of cone orientations for all points
5784 
5785   Level: developer
5786 
5787   Notes:
5788   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points
5789   as returned by `DMPlexGetConeOrientation()`.
5790 
5791   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5792 
5793 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5794 @*/
5795 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5796 {
5797   DM_Plex *mesh = (DM_Plex *)dm->data;
5798 
5799   PetscFunctionBegin;
5800   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5801   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5802   PetscFunctionReturn(PETSC_SUCCESS);
5803 }
5804 
5805 /* FEM Support */
5806 
5807 PetscErrorCode DMPlexGetAllCells_Internal(DM plex, IS *cellIS)
5808 {
5809   PetscInt depth;
5810 
5811   PetscFunctionBegin;
5812   PetscCall(DMPlexGetDepth(plex, &depth));
5813   PetscCall(DMGetStratumIS(plex, "dim", depth, cellIS));
5814   if (!*cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, cellIS));
5815   PetscFunctionReturn(PETSC_SUCCESS);
5816 }
5817 
5818 PetscErrorCode DMPlexGetAllFaces_Internal(DM plex, IS *faceIS)
5819 {
5820   PetscInt depth;
5821 
5822   PetscFunctionBegin;
5823   PetscCall(DMPlexGetDepth(plex, &depth));
5824   PetscCall(DMGetStratumIS(plex, "dim", depth - 1, faceIS));
5825   if (!*faceIS) PetscCall(DMGetStratumIS(plex, "depth", depth - 1, faceIS));
5826   PetscFunctionReturn(PETSC_SUCCESS);
5827 }
5828 
5829 /*
5830  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5831  representing a line in the section.
5832 */
5833 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(DM dm, PetscSection section, PetscInt field, PetscInt line, PetscInt *Nc, PetscInt *k, PetscBool *continuous, PetscBool *tensor)
5834 {
5835   PetscObject  obj;
5836   PetscClassId id;
5837   PetscFE      fe = NULL;
5838 
5839   PetscFunctionBeginHot;
5840   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5841   PetscCall(DMGetField(dm, field, NULL, &obj));
5842   PetscCall(PetscObjectGetClassId(obj, &id));
5843   if (id == PETSCFE_CLASSID) fe = (PetscFE)obj;
5844 
5845   if (!fe) {
5846     /* Assume the full interpolated mesh is in the chart; lines in particular */
5847     /* An order k SEM disc has k-1 dofs on an edge */
5848     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5849     *k = *k / *Nc + 1;
5850   } else {
5851     PetscInt       dual_space_size, dim;
5852     PetscDualSpace dsp;
5853 
5854     PetscCall(DMGetDimension(dm, &dim));
5855     PetscCall(PetscFEGetDualSpace(fe, &dsp));
5856     PetscCall(PetscDualSpaceGetDimension(dsp, &dual_space_size));
5857     *k = (PetscInt)PetscCeilReal(PetscPowReal(dual_space_size / *Nc, 1.0 / dim)) - 1;
5858     PetscCall(PetscDualSpaceLagrangeGetContinuity(dsp, continuous));
5859     PetscCall(PetscDualSpaceLagrangeGetTensor(dsp, tensor));
5860   }
5861   PetscFunctionReturn(PETSC_SUCCESS);
5862 }
5863 
5864 static PetscErrorCode GetFieldSize_Private(PetscInt dim, PetscInt k, PetscBool tensor, PetscInt *dof)
5865 {
5866   PetscFunctionBeginHot;
5867   if (tensor) {
5868     *dof = PetscPowInt(k + 1, dim);
5869   } else {
5870     switch (dim) {
5871     case 1:
5872       *dof = k + 1;
5873       break;
5874     case 2:
5875       *dof = ((k + 1) * (k + 2)) / 2;
5876       break;
5877     case 3:
5878       *dof = ((k + 1) * (k + 2) * (k + 3)) / 6;
5879       break;
5880     default:
5881       *dof = 0;
5882     }
5883   }
5884   PetscFunctionReturn(PETSC_SUCCESS);
5885 }
5886 
5887 /*@
5888   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5889   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5890   section provided (or the section of the `DM`).
5891 
5892   Input Parameters:
5893 + dm      - The `DM`
5894 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5895 - section - The `PetscSection` to reorder, or `NULL` for the default section
5896 
5897   Example:
5898   A typical interpolated single-quad mesh might order points as
5899 .vb
5900   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5901 
5902   v4 -- e6 -- v3
5903   |           |
5904   e7    c0    e8
5905   |           |
5906   v1 -- e5 -- v2
5907 .ve
5908 
5909   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5910   dofs in the order of points, e.g.,
5911 .vb
5912     c0 -> [0,1,2,3]
5913     v1 -> [4]
5914     ...
5915     e5 -> [8, 9]
5916 .ve
5917 
5918   which corresponds to the dofs
5919 .vb
5920     6   10  11  7
5921     13  2   3   15
5922     12  0   1   14
5923     4   8   9   5
5924 .ve
5925 
5926   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5927 .vb
5928   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5929 .ve
5930 
5931   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5932 .vb
5933    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5934 .ve
5935 
5936   Level: developer
5937 
5938   Notes:
5939   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5940   degree of the basis.
5941 
5942   This is required to run with libCEED.
5943 
5944 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5945 @*/
5946 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5947 {
5948   DMLabel   label;
5949   PetscInt  dim, depth = -1, eStart = -1, Nf;
5950   PetscBool continuous = PETSC_TRUE, tensor = PETSC_TRUE;
5951 
5952   PetscFunctionBegin;
5953   PetscCall(DMGetDimension(dm, &dim));
5954   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5955   if (point < 0) {
5956     PetscInt sStart, sEnd;
5957 
5958     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5959     point = sEnd - sStart ? sStart : point;
5960   }
5961   PetscCall(DMPlexGetDepthLabel(dm, &label));
5962   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5963   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5964   if (depth == 1) {
5965     eStart = point;
5966   } else if (depth == dim) {
5967     const PetscInt *cone;
5968 
5969     PetscCall(DMPlexGetCone(dm, point, &cone));
5970     if (dim == 2) eStart = cone[0];
5971     else if (dim == 3) {
5972       const PetscInt *cone2;
5973       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5974       eStart = cone2[0];
5975     } 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);
5976   } 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);
5977 
5978   PetscCall(PetscSectionGetNumFields(section, &Nf));
5979   for (PetscInt d = 1; d <= dim; d++) {
5980     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5981     PetscInt *perm;
5982 
5983     for (f = 0; f < Nf; ++f) {
5984       PetscInt dof;
5985 
5986       PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5987       PetscCheck(dim == 1 || tensor || !continuous, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Continuous field %" PetscInt_FMT " must have a tensor product discretization", f);
5988       if (!continuous && d < dim) continue;
5989       PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5990       size += dof * Nc;
5991     }
5992     PetscCall(PetscMalloc1(size, &perm));
5993     for (f = 0; f < Nf; ++f) {
5994       switch (d) {
5995       case 1:
5996         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5997         if (!continuous && d < dim) continue;
5998         /*
5999          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
6000          We want              [ vtx0; edge of length k-1; vtx1 ]
6001          */
6002         if (continuous) {
6003           for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
6004           for (i = 0; i < k - 1; i++)
6005             for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
6006           for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
6007           foffset = offset;
6008         } else {
6009           PetscInt dof;
6010 
6011           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6012           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6013           foffset = offset;
6014         }
6015         break;
6016       case 2:
6017         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
6018         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6019         if (!continuous && d < dim) continue;
6020         /* The SEM order is
6021 
6022          v_lb, {e_b}, v_rb,
6023          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
6024          v_lt, reverse {e_t}, v_rt
6025          */
6026         if (continuous) {
6027           const PetscInt of   = 0;
6028           const PetscInt oeb  = of + PetscSqr(k - 1);
6029           const PetscInt oer  = oeb + (k - 1);
6030           const PetscInt oet  = oer + (k - 1);
6031           const PetscInt oel  = oet + (k - 1);
6032           const PetscInt ovlb = oel + (k - 1);
6033           const PetscInt ovrb = ovlb + 1;
6034           const PetscInt ovrt = ovrb + 1;
6035           const PetscInt ovlt = ovrt + 1;
6036           PetscInt       o;
6037 
6038           /* bottom */
6039           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
6040           for (o = oeb; o < oer; ++o)
6041             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6042           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
6043           /* middle */
6044           for (i = 0; i < k - 1; ++i) {
6045             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
6046             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
6047               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6048             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
6049           }
6050           /* top */
6051           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
6052           for (o = oel - 1; o >= oet; --o)
6053             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6054           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
6055           foffset = offset;
6056         } else {
6057           PetscInt dof;
6058 
6059           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6060           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6061           foffset = offset;
6062         }
6063         break;
6064       case 3:
6065         /* The original hex closure is
6066 
6067          {c,
6068          f_b, f_t, f_f, f_b, f_r, f_l,
6069          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
6070          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
6071          */
6072         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6073         if (!continuous && d < dim) continue;
6074         /* The SEM order is
6075          Bottom Slice
6076          v_blf, {e^{(k-1)-n}_bf}, v_brf,
6077          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
6078          v_blb, {e_bb}, v_brb,
6079 
6080          Middle Slice (j)
6081          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
6082          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
6083          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
6084 
6085          Top Slice
6086          v_tlf, {e_tf}, v_trf,
6087          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
6088          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
6089          */
6090         if (continuous) {
6091           const PetscInt oc    = 0;
6092           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
6093           const PetscInt oft   = ofb + PetscSqr(k - 1);
6094           const PetscInt off   = oft + PetscSqr(k - 1);
6095           const PetscInt ofk   = off + PetscSqr(k - 1);
6096           const PetscInt ofr   = ofk + PetscSqr(k - 1);
6097           const PetscInt ofl   = ofr + PetscSqr(k - 1);
6098           const PetscInt oebl  = ofl + PetscSqr(k - 1);
6099           const PetscInt oebb  = oebl + (k - 1);
6100           const PetscInt oebr  = oebb + (k - 1);
6101           const PetscInt oebf  = oebr + (k - 1);
6102           const PetscInt oetf  = oebf + (k - 1);
6103           const PetscInt oetr  = oetf + (k - 1);
6104           const PetscInt oetb  = oetr + (k - 1);
6105           const PetscInt oetl  = oetb + (k - 1);
6106           const PetscInt oerf  = oetl + (k - 1);
6107           const PetscInt oelf  = oerf + (k - 1);
6108           const PetscInt oelb  = oelf + (k - 1);
6109           const PetscInt oerb  = oelb + (k - 1);
6110           const PetscInt ovblf = oerb + (k - 1);
6111           const PetscInt ovblb = ovblf + 1;
6112           const PetscInt ovbrb = ovblb + 1;
6113           const PetscInt ovbrf = ovbrb + 1;
6114           const PetscInt ovtlf = ovbrf + 1;
6115           const PetscInt ovtrf = ovtlf + 1;
6116           const PetscInt ovtrb = ovtrf + 1;
6117           const PetscInt ovtlb = ovtrb + 1;
6118           PetscInt       o, n;
6119 
6120           /* Bottom Slice */
6121           /*   bottom */
6122           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
6123           for (o = oetf - 1; o >= oebf; --o)
6124             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6125           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
6126           /*   middle */
6127           for (i = 0; i < k - 1; ++i) {
6128             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
6129             for (n = 0; n < k - 1; ++n) {
6130               o = ofb + n * (k - 1) + i;
6131               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6132             }
6133             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
6134           }
6135           /*   top */
6136           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
6137           for (o = oebb; o < oebr; ++o)
6138             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6139           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
6140 
6141           /* Middle Slice */
6142           for (j = 0; j < k - 1; ++j) {
6143             /*   bottom */
6144             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
6145             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
6146               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6147             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
6148             /*   middle */
6149             for (i = 0; i < k - 1; ++i) {
6150               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
6151               for (n = 0; n < k - 1; ++n)
6152                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
6153               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
6154             }
6155             /*   top */
6156             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
6157             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
6158               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6159             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
6160           }
6161 
6162           /* Top Slice */
6163           /*   bottom */
6164           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
6165           for (o = oetf; o < oetr; ++o)
6166             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6167           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
6168           /*   middle */
6169           for (i = 0; i < k - 1; ++i) {
6170             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
6171             for (n = 0; n < k - 1; ++n)
6172               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
6173             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
6174           }
6175           /*   top */
6176           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
6177           for (o = oetl - 1; o >= oetb; --o)
6178             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6179           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
6180 
6181           foffset = offset;
6182         } else {
6183           PetscInt dof;
6184 
6185           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6186           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6187           foffset = offset;
6188         }
6189         break;
6190       default:
6191         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
6192       }
6193     }
6194     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
6195     /* Check permutation */
6196     {
6197       PetscInt *check;
6198 
6199       PetscCall(PetscMalloc1(size, &check));
6200       for (i = 0; i < size; ++i) {
6201         check[i] = -1;
6202         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
6203       }
6204       for (i = 0; i < size; ++i) check[perm[i]] = i;
6205       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
6206       PetscCall(PetscFree(check));
6207     }
6208     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
6209     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
6210       PetscInt *loc_perm;
6211       PetscCall(PetscMalloc1(size * 2, &loc_perm));
6212       for (PetscInt i = 0; i < size; i++) {
6213         loc_perm[i]        = perm[i];
6214         loc_perm[size + i] = size + perm[i];
6215       }
6216       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
6217     }
6218   }
6219   PetscFunctionReturn(PETSC_SUCCESS);
6220 }
6221 
6222 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
6223 {
6224   PetscDS  prob;
6225   PetscInt depth, Nf, h;
6226   DMLabel  label;
6227 
6228   PetscFunctionBeginHot;
6229   PetscCall(DMGetDS(dm, &prob));
6230   Nf      = prob->Nf;
6231   label   = dm->depthLabel;
6232   *dspace = NULL;
6233   if (field < Nf) {
6234     PetscObject disc = prob->disc[field];
6235 
6236     if (disc->classid == PETSCFE_CLASSID) {
6237       PetscDualSpace dsp;
6238 
6239       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
6240       PetscCall(DMLabelGetNumValues(label, &depth));
6241       PetscCall(DMLabelGetValue(label, point, &h));
6242       h = depth - 1 - h;
6243       if (h) {
6244         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
6245       } else {
6246         *dspace = dsp;
6247       }
6248     }
6249   }
6250   PetscFunctionReturn(PETSC_SUCCESS);
6251 }
6252 
6253 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6254 {
6255   PetscScalar       *array;
6256   const PetscScalar *vArray;
6257   const PetscInt    *cone, *coneO;
6258   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
6259 
6260   PetscFunctionBeginHot;
6261   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6262   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6263   PetscCall(DMPlexGetCone(dm, point, &cone));
6264   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6265   if (!values || !*values) {
6266     if ((point >= pStart) && (point < pEnd)) {
6267       PetscInt dof;
6268 
6269       PetscCall(PetscSectionGetDof(section, point, &dof));
6270       size += dof;
6271     }
6272     for (p = 0; p < numPoints; ++p) {
6273       const PetscInt cp = cone[p];
6274       PetscInt       dof;
6275 
6276       if ((cp < pStart) || (cp >= pEnd)) continue;
6277       PetscCall(PetscSectionGetDof(section, cp, &dof));
6278       size += dof;
6279     }
6280     if (!values) {
6281       if (csize) *csize = size;
6282       PetscFunctionReturn(PETSC_SUCCESS);
6283     }
6284     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
6285   } else {
6286     array = *values;
6287   }
6288   size = 0;
6289   PetscCall(VecGetArrayRead(v, &vArray));
6290   if ((point >= pStart) && (point < pEnd)) {
6291     PetscInt           dof, off, d;
6292     const PetscScalar *varr;
6293 
6294     PetscCall(PetscSectionGetDof(section, point, &dof));
6295     PetscCall(PetscSectionGetOffset(section, point, &off));
6296     varr = PetscSafePointerPlusOffset(vArray, off);
6297     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6298     size += dof;
6299   }
6300   for (p = 0; p < numPoints; ++p) {
6301     const PetscInt     cp = cone[p];
6302     PetscInt           o  = coneO[p];
6303     PetscInt           dof, off, d;
6304     const PetscScalar *varr;
6305 
6306     if ((cp < pStart) || (cp >= pEnd)) continue;
6307     PetscCall(PetscSectionGetDof(section, cp, &dof));
6308     PetscCall(PetscSectionGetOffset(section, cp, &off));
6309     varr = PetscSafePointerPlusOffset(vArray, off);
6310     if (o >= 0) {
6311       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6312     } else {
6313       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
6314     }
6315     size += dof;
6316   }
6317   PetscCall(VecRestoreArrayRead(v, &vArray));
6318   if (!*values) {
6319     if (csize) *csize = size;
6320     *values = array;
6321   } else {
6322     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6323     *csize = size;
6324   }
6325   PetscFunctionReturn(PETSC_SUCCESS);
6326 }
6327 
6328 /* Compress out points not in the section */
6329 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
6330 {
6331   const PetscInt np = *numPoints;
6332   PetscInt       pStart, pEnd, p, q;
6333 
6334   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6335   for (p = 0, q = 0; p < np; ++p) {
6336     const PetscInt r = points[p * 2];
6337     if ((r >= pStart) && (r < pEnd)) {
6338       points[q * 2]     = r;
6339       points[q * 2 + 1] = points[p * 2 + 1];
6340       ++q;
6341     }
6342   }
6343   *numPoints = q;
6344   return PETSC_SUCCESS;
6345 }
6346 
6347 /* Compressed closure does not apply closure permutation */
6348 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6349 {
6350   const PetscInt *cla = NULL;
6351   PetscInt        np, *pts = NULL;
6352 
6353   PetscFunctionBeginHot;
6354   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
6355   if (!ornt && *clPoints) {
6356     PetscInt dof, off;
6357 
6358     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
6359     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
6360     PetscCall(ISGetIndices(*clPoints, &cla));
6361     np  = dof / 2;
6362     pts = PetscSafePointerPlusOffset((PetscInt *)cla, off);
6363   } else {
6364     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
6365     PetscCall(CompressPoints_Private(section, &np, pts));
6366   }
6367   *numPoints = np;
6368   *points    = pts;
6369   *clp       = cla;
6370   PetscFunctionReturn(PETSC_SUCCESS);
6371 }
6372 
6373 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6374 {
6375   PetscFunctionBeginHot;
6376   if (!*clPoints) {
6377     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
6378   } else {
6379     PetscCall(ISRestoreIndices(*clPoints, clp));
6380   }
6381   *numPoints = 0;
6382   *points    = NULL;
6383   *clSec     = NULL;
6384   *clPoints  = NULL;
6385   *clp       = NULL;
6386   PetscFunctionReturn(PETSC_SUCCESS);
6387 }
6388 
6389 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
6390 {
6391   PetscInt            offset = 0, p;
6392   const PetscInt    **perms  = NULL;
6393   const PetscScalar **flips  = NULL;
6394 
6395   PetscFunctionBeginHot;
6396   *size = 0;
6397   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6398   for (p = 0; p < numPoints; p++) {
6399     const PetscInt     point = points[2 * p];
6400     const PetscInt    *perm  = perms ? perms[p] : NULL;
6401     const PetscScalar *flip  = flips ? flips[p] : NULL;
6402     PetscInt           dof, off, d;
6403     const PetscScalar *varr;
6404 
6405     PetscCall(PetscSectionGetDof(section, point, &dof));
6406     PetscCall(PetscSectionGetOffset(section, point, &off));
6407     varr = PetscSafePointerPlusOffset(vArray, off);
6408     if (clperm) {
6409       if (perm) {
6410         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
6411       } else {
6412         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
6413       }
6414       if (flip) {
6415         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
6416       }
6417     } else {
6418       if (perm) {
6419         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
6420       } else {
6421         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
6422       }
6423       if (flip) {
6424         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
6425       }
6426     }
6427     offset += dof;
6428   }
6429   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6430   *size = offset;
6431   PetscFunctionReturn(PETSC_SUCCESS);
6432 }
6433 
6434 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[])
6435 {
6436   PetscInt offset = 0, f;
6437 
6438   PetscFunctionBeginHot;
6439   *size = 0;
6440   for (f = 0; f < numFields; ++f) {
6441     PetscInt            p;
6442     const PetscInt    **perms = NULL;
6443     const PetscScalar **flips = NULL;
6444 
6445     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6446     for (p = 0; p < numPoints; p++) {
6447       const PetscInt     point = points[2 * p];
6448       PetscInt           fdof, foff, b;
6449       const PetscScalar *varr;
6450       const PetscInt    *perm = perms ? perms[p] : NULL;
6451       const PetscScalar *flip = flips ? flips[p] : NULL;
6452 
6453       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6454       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6455       varr = &vArray[foff];
6456       if (clperm) {
6457         if (perm) {
6458           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6459         } else {
6460           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6461         }
6462         if (flip) {
6463           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6464         }
6465       } else {
6466         if (perm) {
6467           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6468         } else {
6469           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6470         }
6471         if (flip) {
6472           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6473         }
6474       }
6475       offset += fdof;
6476     }
6477     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6478   }
6479   *size = offset;
6480   PetscFunctionReturn(PETSC_SUCCESS);
6481 }
6482 
6483 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, PetscBool useClPerm, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6484 {
6485   PetscSection    clSection;
6486   IS              clPoints;
6487   PetscInt       *points = NULL;
6488   const PetscInt *clp, *perm = NULL;
6489   PetscInt        depth, numFields, numPoints, asize;
6490 
6491   PetscFunctionBeginHot;
6492   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6493   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6494   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6495   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
6496   PetscCall(DMPlexGetDepth(dm, &depth));
6497   PetscCall(PetscSectionGetNumFields(section, &numFields));
6498   if (depth == 1 && numFields < 2) {
6499     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6500     PetscFunctionReturn(PETSC_SUCCESS);
6501   }
6502   /* Get points */
6503   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6504   /* Get sizes */
6505   asize = 0;
6506   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6507     PetscInt dof;
6508     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6509     asize += dof;
6510   }
6511   if (values) {
6512     const PetscScalar *vArray;
6513     PetscInt           size;
6514 
6515     if (*values) {
6516       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);
6517     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6518     if (useClPerm) PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6519     PetscCall(VecGetArrayRead(v, &vArray));
6520     /* Get values */
6521     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6522     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6523     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6524     /* Cleanup array */
6525     PetscCall(VecRestoreArrayRead(v, &vArray));
6526   }
6527   if (csize) *csize = asize;
6528   /* Cleanup points */
6529   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6530   PetscFunctionReturn(PETSC_SUCCESS);
6531 }
6532 
6533 /*@C
6534   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6535 
6536   Not collective
6537 
6538   Input Parameters:
6539 + dm      - The `DM`
6540 . section - The section describing the layout in `v`, or `NULL` to use the default section
6541 . v       - The local vector
6542 - point   - The point in the `DM`
6543 
6544   Input/Output Parameters:
6545 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6546 - values - An array to use for the values, or *values = `NULL` to have it allocated automatically;
6547            if the user provided `NULL`, it is a borrowed array and should not be freed, use  `DMPlexVecRestoreClosure()` to return it
6548 
6549   Level: intermediate
6550 
6551   Notes:
6552   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6553   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6554   assembly function, and a user may already have allocated storage for this operation.
6555 
6556   A typical use could be
6557 .vb
6558    values = NULL;
6559    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6560    for (cl = 0; cl < clSize; ++cl) {
6561      <Compute on closure>
6562    }
6563    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6564 .ve
6565   or
6566 .vb
6567    PetscMalloc1(clMaxSize, &values);
6568    for (p = pStart; p < pEnd; ++p) {
6569      clSize = clMaxSize;
6570      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6571      for (cl = 0; cl < clSize; ++cl) {
6572        <Compute on closure>
6573      }
6574    }
6575    PetscFree(values);
6576 .ve
6577 
6578   Fortran Notes:
6579   The `csize` argument is not present in the Fortran binding.
6580 
6581   `values` must be declared with
6582 .vb
6583   PetscScalar,dimension(:),pointer   :: values
6584 .ve
6585   and it will be allocated internally by PETSc to hold the values returned
6586 
6587 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6588 @*/
6589 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6590 {
6591   PetscFunctionBeginHot;
6592   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6593   PetscFunctionReturn(PETSC_SUCCESS);
6594 }
6595 
6596 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6597 {
6598   DMLabel            depthLabel;
6599   PetscSection       clSection;
6600   IS                 clPoints;
6601   PetscScalar       *array;
6602   const PetscScalar *vArray;
6603   PetscInt          *points = NULL;
6604   const PetscInt    *clp, *perm = NULL;
6605   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6606 
6607   PetscFunctionBeginHot;
6608   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6609   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6610   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6611   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6612   PetscCall(DMPlexGetDepth(dm, &mdepth));
6613   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6614   PetscCall(PetscSectionGetNumFields(section, &numFields));
6615   if (mdepth == 1 && numFields < 2) {
6616     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6617     PetscFunctionReturn(PETSC_SUCCESS);
6618   }
6619   /* Get points */
6620   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6621   for (clsize = 0, p = 0; p < Np; p++) {
6622     PetscInt dof;
6623     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6624     clsize += dof;
6625   }
6626   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6627   /* Filter points */
6628   for (p = 0; p < numPoints * 2; p += 2) {
6629     PetscInt dep;
6630 
6631     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6632     if (dep != depth) continue;
6633     points[Np * 2 + 0] = points[p];
6634     points[Np * 2 + 1] = points[p + 1];
6635     ++Np;
6636   }
6637   /* Get array */
6638   if (!values || !*values) {
6639     PetscInt asize = 0, dof;
6640 
6641     for (p = 0; p < Np * 2; p += 2) {
6642       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6643       asize += dof;
6644     }
6645     if (!values) {
6646       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6647       if (csize) *csize = asize;
6648       PetscFunctionReturn(PETSC_SUCCESS);
6649     }
6650     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6651   } else {
6652     array = *values;
6653   }
6654   PetscCall(VecGetArrayRead(v, &vArray));
6655   /* Get values */
6656   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6657   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6658   /* Cleanup points */
6659   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6660   /* Cleanup array */
6661   PetscCall(VecRestoreArrayRead(v, &vArray));
6662   if (!*values) {
6663     if (csize) *csize = size;
6664     *values = array;
6665   } else {
6666     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6667     *csize = size;
6668   }
6669   PetscFunctionReturn(PETSC_SUCCESS);
6670 }
6671 
6672 /*@C
6673   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point' obtained with `DMPlexVecGetClosure()`
6674 
6675   Not collective
6676 
6677   Input Parameters:
6678 + dm      - The `DM`
6679 . section - The section describing the layout in `v`, or `NULL` to use the default section
6680 . v       - The local vector
6681 . point   - The point in the `DM`
6682 . csize   - The number of values in the closure, or `NULL`
6683 - values  - The array of values
6684 
6685   Level: intermediate
6686 
6687   Note:
6688   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6689 
6690   Fortran Note:
6691   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6692 
6693 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6694 @*/
6695 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6696 {
6697   PetscInt size = 0;
6698 
6699   PetscFunctionBegin;
6700   /* Should work without recalculating size */
6701   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6702   *values = NULL;
6703   PetscFunctionReturn(PETSC_SUCCESS);
6704 }
6705 
6706 static inline void add(PetscScalar *x, PetscScalar y)
6707 {
6708   *x += y;
6709 }
6710 static inline void insert(PetscScalar *x, PetscScalar y)
6711 {
6712   *x = y;
6713 }
6714 
6715 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[])
6716 {
6717   PetscInt        cdof;  /* The number of constraints on this point */
6718   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6719   PetscScalar    *a;
6720   PetscInt        off, cind = 0, k;
6721 
6722   PetscFunctionBegin;
6723   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6724   PetscCall(PetscSectionGetOffset(section, point, &off));
6725   a = &array[off];
6726   if (!cdof || setBC) {
6727     if (clperm) {
6728       if (perm) {
6729         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6730       } else {
6731         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6732       }
6733     } else {
6734       if (perm) {
6735         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6736       } else {
6737         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6738       }
6739     }
6740   } else {
6741     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6742     if (clperm) {
6743       if (perm) {
6744         for (k = 0; k < dof; ++k) {
6745           if ((cind < cdof) && (k == cdofs[cind])) {
6746             ++cind;
6747             continue;
6748           }
6749           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6750         }
6751       } else {
6752         for (k = 0; k < dof; ++k) {
6753           if ((cind < cdof) && (k == cdofs[cind])) {
6754             ++cind;
6755             continue;
6756           }
6757           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6758         }
6759       }
6760     } else {
6761       if (perm) {
6762         for (k = 0; k < dof; ++k) {
6763           if ((cind < cdof) && (k == cdofs[cind])) {
6764             ++cind;
6765             continue;
6766           }
6767           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6768         }
6769       } else {
6770         for (k = 0; k < dof; ++k) {
6771           if ((cind < cdof) && (k == cdofs[cind])) {
6772             ++cind;
6773             continue;
6774           }
6775           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6776         }
6777       }
6778     }
6779   }
6780   PetscFunctionReturn(PETSC_SUCCESS);
6781 }
6782 
6783 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[])
6784 {
6785   PetscInt        cdof;  /* The number of constraints on this point */
6786   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6787   PetscScalar    *a;
6788   PetscInt        off, cind = 0, k;
6789 
6790   PetscFunctionBegin;
6791   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6792   PetscCall(PetscSectionGetOffset(section, point, &off));
6793   a = &array[off];
6794   if (cdof) {
6795     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6796     if (clperm) {
6797       if (perm) {
6798         for (k = 0; k < dof; ++k) {
6799           if ((cind < cdof) && (k == cdofs[cind])) {
6800             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6801             cind++;
6802           }
6803         }
6804       } else {
6805         for (k = 0; k < dof; ++k) {
6806           if ((cind < cdof) && (k == cdofs[cind])) {
6807             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6808             cind++;
6809           }
6810         }
6811       }
6812     } else {
6813       if (perm) {
6814         for (k = 0; k < dof; ++k) {
6815           if ((cind < cdof) && (k == cdofs[cind])) {
6816             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6817             cind++;
6818           }
6819         }
6820       } else {
6821         for (k = 0; k < dof; ++k) {
6822           if ((cind < cdof) && (k == cdofs[cind])) {
6823             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6824             cind++;
6825           }
6826         }
6827       }
6828     }
6829   }
6830   PetscFunctionReturn(PETSC_SUCCESS);
6831 }
6832 
6833 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[])
6834 {
6835   PetscScalar    *a;
6836   PetscInt        fdof, foff, fcdof, foffset = *offset;
6837   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6838   PetscInt        cind = 0, b;
6839 
6840   PetscFunctionBegin;
6841   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6842   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6843   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6844   a = &array[foff];
6845   if (!fcdof || setBC) {
6846     if (clperm) {
6847       if (perm) {
6848         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6849       } else {
6850         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6851       }
6852     } else {
6853       if (perm) {
6854         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6855       } else {
6856         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6857       }
6858     }
6859   } else {
6860     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6861     if (clperm) {
6862       if (perm) {
6863         for (b = 0; b < fdof; b++) {
6864           if ((cind < fcdof) && (b == fcdofs[cind])) {
6865             ++cind;
6866             continue;
6867           }
6868           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6869         }
6870       } else {
6871         for (b = 0; b < fdof; b++) {
6872           if ((cind < fcdof) && (b == fcdofs[cind])) {
6873             ++cind;
6874             continue;
6875           }
6876           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6877         }
6878       }
6879     } else {
6880       if (perm) {
6881         for (b = 0; b < fdof; b++) {
6882           if ((cind < fcdof) && (b == fcdofs[cind])) {
6883             ++cind;
6884             continue;
6885           }
6886           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6887         }
6888       } else {
6889         for (b = 0; b < fdof; b++) {
6890           if ((cind < fcdof) && (b == fcdofs[cind])) {
6891             ++cind;
6892             continue;
6893           }
6894           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6895         }
6896       }
6897     }
6898   }
6899   *offset += fdof;
6900   PetscFunctionReturn(PETSC_SUCCESS);
6901 }
6902 
6903 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[])
6904 {
6905   PetscScalar    *a;
6906   PetscInt        fdof, foff, fcdof, foffset = *offset;
6907   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6908   PetscInt        Nc, cind = 0, ncind = 0, b;
6909   PetscBool       ncSet, fcSet;
6910 
6911   PetscFunctionBegin;
6912   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6913   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6914   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6915   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6916   a = &array[foff];
6917   if (fcdof) {
6918     /* We just override fcdof and fcdofs with Ncc and comps */
6919     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6920     if (clperm) {
6921       if (perm) {
6922         if (comps) {
6923           for (b = 0; b < fdof; b++) {
6924             ncSet = fcSet = PETSC_FALSE;
6925             if (b % Nc == comps[ncind]) {
6926               ncind = (ncind + 1) % Ncc;
6927               ncSet = PETSC_TRUE;
6928             }
6929             if ((cind < fcdof) && (b == fcdofs[cind])) {
6930               ++cind;
6931               fcSet = PETSC_TRUE;
6932             }
6933             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6934           }
6935         } else {
6936           for (b = 0; b < fdof; b++) {
6937             if ((cind < fcdof) && (b == fcdofs[cind])) {
6938               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6939               ++cind;
6940             }
6941           }
6942         }
6943       } else {
6944         if (comps) {
6945           for (b = 0; b < fdof; b++) {
6946             ncSet = fcSet = PETSC_FALSE;
6947             if (b % Nc == comps[ncind]) {
6948               ncind = (ncind + 1) % Ncc;
6949               ncSet = PETSC_TRUE;
6950             }
6951             if ((cind < fcdof) && (b == fcdofs[cind])) {
6952               ++cind;
6953               fcSet = PETSC_TRUE;
6954             }
6955             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6956           }
6957         } else {
6958           for (b = 0; b < fdof; b++) {
6959             if ((cind < fcdof) && (b == fcdofs[cind])) {
6960               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6961               ++cind;
6962             }
6963           }
6964         }
6965       }
6966     } else {
6967       if (perm) {
6968         if (comps) {
6969           for (b = 0; b < fdof; b++) {
6970             ncSet = fcSet = PETSC_FALSE;
6971             if (b % Nc == comps[ncind]) {
6972               ncind = (ncind + 1) % Ncc;
6973               ncSet = PETSC_TRUE;
6974             }
6975             if ((cind < fcdof) && (b == fcdofs[cind])) {
6976               ++cind;
6977               fcSet = PETSC_TRUE;
6978             }
6979             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6980           }
6981         } else {
6982           for (b = 0; b < fdof; b++) {
6983             if ((cind < fcdof) && (b == fcdofs[cind])) {
6984               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6985               ++cind;
6986             }
6987           }
6988         }
6989       } else {
6990         if (comps) {
6991           for (b = 0; b < fdof; b++) {
6992             ncSet = fcSet = PETSC_FALSE;
6993             if (b % Nc == comps[ncind]) {
6994               ncind = (ncind + 1) % Ncc;
6995               ncSet = PETSC_TRUE;
6996             }
6997             if ((cind < fcdof) && (b == fcdofs[cind])) {
6998               ++cind;
6999               fcSet = PETSC_TRUE;
7000             }
7001             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
7002           }
7003         } else {
7004           for (b = 0; b < fdof; b++) {
7005             if ((cind < fcdof) && (b == fcdofs[cind])) {
7006               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
7007               ++cind;
7008             }
7009           }
7010         }
7011       }
7012     }
7013   }
7014   *offset += fdof;
7015   PetscFunctionReturn(PETSC_SUCCESS);
7016 }
7017 
7018 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7019 {
7020   PetscScalar    *array;
7021   const PetscInt *cone, *coneO;
7022   PetscInt        pStart, pEnd, p, numPoints, off, dof;
7023 
7024   PetscFunctionBeginHot;
7025   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
7026   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
7027   PetscCall(DMPlexGetCone(dm, point, &cone));
7028   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
7029   PetscCall(VecGetArray(v, &array));
7030   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
7031     const PetscInt cp = !p ? point : cone[p - 1];
7032     const PetscInt o  = !p ? 0 : coneO[p - 1];
7033 
7034     if ((cp < pStart) || (cp >= pEnd)) {
7035       dof = 0;
7036       continue;
7037     }
7038     PetscCall(PetscSectionGetDof(section, cp, &dof));
7039     /* ADD_VALUES */
7040     {
7041       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7042       PetscScalar    *a;
7043       PetscInt        cdof, coff, cind = 0, k;
7044 
7045       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
7046       PetscCall(PetscSectionGetOffset(section, cp, &coff));
7047       a = &array[coff];
7048       if (!cdof) {
7049         if (o >= 0) {
7050           for (k = 0; k < dof; ++k) a[k] += values[off + k];
7051         } else {
7052           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
7053         }
7054       } else {
7055         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
7056         if (o >= 0) {
7057           for (k = 0; k < dof; ++k) {
7058             if ((cind < cdof) && (k == cdofs[cind])) {
7059               ++cind;
7060               continue;
7061             }
7062             a[k] += values[off + k];
7063           }
7064         } else {
7065           for (k = 0; k < dof; ++k) {
7066             if ((cind < cdof) && (k == cdofs[cind])) {
7067               ++cind;
7068               continue;
7069             }
7070             a[k] += values[off + dof - k - 1];
7071           }
7072         }
7073       }
7074     }
7075   }
7076   PetscCall(VecRestoreArray(v, &array));
7077   PetscFunctionReturn(PETSC_SUCCESS);
7078 }
7079 
7080 /*@C
7081   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
7082 
7083   Not collective
7084 
7085   Input Parameters:
7086 + dm      - The `DM`
7087 . section - The section describing the layout in `v`, or `NULL` to use the default section
7088 . v       - The local vector
7089 . point   - The point in the `DM`
7090 . values  - The array of values
7091 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
7092             where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
7093 
7094   Level: intermediate
7095 
7096   Note:
7097   Usually the input arrays were obtained with `DMPlexVecGetClosure()`
7098 
7099   Fortran Note:
7100   `values` must be declared with
7101 .vb
7102   PetscScalar,dimension(:),pointer   :: values
7103 .ve
7104 
7105 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
7106 @*/
7107 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7108 {
7109   PetscSection    clSection;
7110   IS              clPoints;
7111   PetscScalar    *array;
7112   PetscInt       *points = NULL;
7113   const PetscInt *clp, *clperm = NULL;
7114   PetscInt        depth, numFields, numPoints, p, clsize;
7115 
7116   PetscFunctionBeginHot;
7117   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7118   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7119   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7120   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7121   PetscCall(DMPlexGetDepth(dm, &depth));
7122   PetscCall(PetscSectionGetNumFields(section, &numFields));
7123   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
7124     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
7125     PetscFunctionReturn(PETSC_SUCCESS);
7126   }
7127   /* Get points */
7128   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7129   for (clsize = 0, p = 0; p < numPoints; p++) {
7130     PetscInt dof;
7131     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7132     clsize += dof;
7133   }
7134   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7135   /* Get array */
7136   PetscCall(VecGetArray(v, &array));
7137   /* Get values */
7138   if (numFields > 0) {
7139     PetscInt offset = 0, f;
7140     for (f = 0; f < numFields; ++f) {
7141       const PetscInt    **perms = NULL;
7142       const PetscScalar **flips = NULL;
7143 
7144       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7145       switch (mode) {
7146       case INSERT_VALUES:
7147         for (p = 0; p < numPoints; p++) {
7148           const PetscInt     point = points[2 * p];
7149           const PetscInt    *perm  = perms ? perms[p] : NULL;
7150           const PetscScalar *flip  = flips ? flips[p] : NULL;
7151           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
7152         }
7153         break;
7154       case INSERT_ALL_VALUES:
7155         for (p = 0; p < numPoints; p++) {
7156           const PetscInt     point = points[2 * p];
7157           const PetscInt    *perm  = perms ? perms[p] : NULL;
7158           const PetscScalar *flip  = flips ? flips[p] : NULL;
7159           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
7160         }
7161         break;
7162       case INSERT_BC_VALUES:
7163         for (p = 0; p < numPoints; p++) {
7164           const PetscInt     point = points[2 * p];
7165           const PetscInt    *perm  = perms ? perms[p] : NULL;
7166           const PetscScalar *flip  = flips ? flips[p] : NULL;
7167           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
7168         }
7169         break;
7170       case ADD_VALUES:
7171         for (p = 0; p < numPoints; p++) {
7172           const PetscInt     point = points[2 * p];
7173           const PetscInt    *perm  = perms ? perms[p] : NULL;
7174           const PetscScalar *flip  = flips ? flips[p] : NULL;
7175           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
7176         }
7177         break;
7178       case ADD_ALL_VALUES:
7179         for (p = 0; p < numPoints; p++) {
7180           const PetscInt     point = points[2 * p];
7181           const PetscInt    *perm  = perms ? perms[p] : NULL;
7182           const PetscScalar *flip  = flips ? flips[p] : NULL;
7183           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
7184         }
7185         break;
7186       case ADD_BC_VALUES:
7187         for (p = 0; p < numPoints; p++) {
7188           const PetscInt     point = points[2 * p];
7189           const PetscInt    *perm  = perms ? perms[p] : NULL;
7190           const PetscScalar *flip  = flips ? flips[p] : NULL;
7191           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
7192         }
7193         break;
7194       default:
7195         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7196       }
7197       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7198     }
7199   } else {
7200     PetscInt            dof, off;
7201     const PetscInt    **perms = NULL;
7202     const PetscScalar **flips = NULL;
7203 
7204     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
7205     switch (mode) {
7206     case INSERT_VALUES:
7207       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7208         const PetscInt     point = points[2 * p];
7209         const PetscInt    *perm  = perms ? perms[p] : NULL;
7210         const PetscScalar *flip  = flips ? flips[p] : NULL;
7211         PetscCall(PetscSectionGetDof(section, point, &dof));
7212         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
7213       }
7214       break;
7215     case INSERT_ALL_VALUES:
7216       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7217         const PetscInt     point = points[2 * p];
7218         const PetscInt    *perm  = perms ? perms[p] : NULL;
7219         const PetscScalar *flip  = flips ? flips[p] : NULL;
7220         PetscCall(PetscSectionGetDof(section, point, &dof));
7221         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
7222       }
7223       break;
7224     case INSERT_BC_VALUES:
7225       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7226         const PetscInt     point = points[2 * p];
7227         const PetscInt    *perm  = perms ? perms[p] : NULL;
7228         const PetscScalar *flip  = flips ? flips[p] : NULL;
7229         PetscCall(PetscSectionGetDof(section, point, &dof));
7230         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
7231       }
7232       break;
7233     case ADD_VALUES:
7234       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
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(PetscSectionGetDof(section, point, &dof));
7239         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
7240       }
7241       break;
7242     case ADD_ALL_VALUES:
7243       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7244         const PetscInt     point = points[2 * p];
7245         const PetscInt    *perm  = perms ? perms[p] : NULL;
7246         const PetscScalar *flip  = flips ? flips[p] : NULL;
7247         PetscCall(PetscSectionGetDof(section, point, &dof));
7248         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
7249       }
7250       break;
7251     case ADD_BC_VALUES:
7252       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7253         const PetscInt     point = points[2 * p];
7254         const PetscInt    *perm  = perms ? perms[p] : NULL;
7255         const PetscScalar *flip  = flips ? flips[p] : NULL;
7256         PetscCall(PetscSectionGetDof(section, point, &dof));
7257         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
7258       }
7259       break;
7260     default:
7261       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7262     }
7263     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
7264   }
7265   /* Cleanup points */
7266   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7267   /* Cleanup array */
7268   PetscCall(VecRestoreArray(v, &array));
7269   PetscFunctionReturn(PETSC_SUCCESS);
7270 }
7271 
7272 /* Check whether the given point is in the label. If not, update the offset to skip this point */
7273 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
7274 {
7275   PetscFunctionBegin;
7276   *contains = PETSC_TRUE;
7277   if (label) {
7278     PetscInt fdof;
7279 
7280     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
7281     if (!*contains) {
7282       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7283       *offset += fdof;
7284       PetscFunctionReturn(PETSC_SUCCESS);
7285     }
7286   }
7287   PetscFunctionReturn(PETSC_SUCCESS);
7288 }
7289 
7290 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
7291 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)
7292 {
7293   PetscSection    clSection;
7294   IS              clPoints;
7295   PetscScalar    *array;
7296   PetscInt       *points = NULL;
7297   const PetscInt *clp;
7298   PetscInt        numFields, numPoints, p;
7299   PetscInt        offset = 0, f;
7300 
7301   PetscFunctionBeginHot;
7302   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7303   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7304   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7305   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7306   PetscCall(PetscSectionGetNumFields(section, &numFields));
7307   /* Get points */
7308   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7309   /* Get array */
7310   PetscCall(VecGetArray(v, &array));
7311   /* Get values */
7312   for (f = 0; f < numFields; ++f) {
7313     const PetscInt    **perms = NULL;
7314     const PetscScalar **flips = NULL;
7315     PetscBool           contains;
7316 
7317     if (!fieldActive[f]) {
7318       for (p = 0; p < numPoints * 2; p += 2) {
7319         PetscInt fdof;
7320         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7321         offset += fdof;
7322       }
7323       continue;
7324     }
7325     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7326     switch (mode) {
7327     case INSERT_VALUES:
7328       for (p = 0; p < numPoints; p++) {
7329         const PetscInt     point = points[2 * p];
7330         const PetscInt    *perm  = perms ? perms[p] : NULL;
7331         const PetscScalar *flip  = flips ? flips[p] : NULL;
7332         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7333         if (!contains) continue;
7334         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
7335       }
7336       break;
7337     case INSERT_ALL_VALUES:
7338       for (p = 0; p < numPoints; p++) {
7339         const PetscInt     point = points[2 * p];
7340         const PetscInt    *perm  = perms ? perms[p] : NULL;
7341         const PetscScalar *flip  = flips ? flips[p] : NULL;
7342         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7343         if (!contains) continue;
7344         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
7345       }
7346       break;
7347     case INSERT_BC_VALUES:
7348       for (p = 0; p < numPoints; p++) {
7349         const PetscInt     point = points[2 * p];
7350         const PetscInt    *perm  = perms ? perms[p] : NULL;
7351         const PetscScalar *flip  = flips ? flips[p] : NULL;
7352         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7353         if (!contains) continue;
7354         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
7355       }
7356       break;
7357     case ADD_VALUES:
7358       for (p = 0; p < numPoints; p++) {
7359         const PetscInt     point = points[2 * p];
7360         const PetscInt    *perm  = perms ? perms[p] : NULL;
7361         const PetscScalar *flip  = flips ? flips[p] : NULL;
7362         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7363         if (!contains) continue;
7364         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
7365       }
7366       break;
7367     case ADD_ALL_VALUES:
7368       for (p = 0; p < numPoints; p++) {
7369         const PetscInt     point = points[2 * p];
7370         const PetscInt    *perm  = perms ? perms[p] : NULL;
7371         const PetscScalar *flip  = flips ? flips[p] : NULL;
7372         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7373         if (!contains) continue;
7374         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
7375       }
7376       break;
7377     default:
7378       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7379     }
7380     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7381   }
7382   /* Cleanup points */
7383   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7384   /* Cleanup array */
7385   PetscCall(VecRestoreArray(v, &array));
7386   PetscFunctionReturn(PETSC_SUCCESS);
7387 }
7388 
7389 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
7390 {
7391   PetscMPIInt rank;
7392   PetscInt    i, j;
7393 
7394   PetscFunctionBegin;
7395   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7396   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
7397   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
7398   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
7399   numCIndices = numCIndices ? numCIndices : numRIndices;
7400   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
7401   for (i = 0; i < numRIndices; i++) {
7402     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
7403     for (j = 0; j < numCIndices; j++) {
7404 #if defined(PETSC_USE_COMPLEX)
7405       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
7406 #else
7407       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
7408 #endif
7409     }
7410     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
7411   }
7412   PetscFunctionReturn(PETSC_SUCCESS);
7413 }
7414 
7415 /*
7416   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
7417 
7418   Input Parameters:
7419 + section - The section for this data layout
7420 . islocal - Is the section (and thus indices being requested) local or global?
7421 . point   - The point contributing dofs with these indices
7422 . off     - The global offset of this point
7423 . loff    - The local offset of each field
7424 . setBC   - The flag determining whether to include indices of boundary values
7425 . perm    - A permutation of the dofs on this point, or NULL
7426 - indperm - A permutation of the entire indices array, or NULL
7427 
7428   Output Parameter:
7429 . indices - Indices for dofs on this point
7430 
7431   Level: developer
7432 
7433   Note: The indices could be local or global, depending on the value of 'off'.
7434 */
7435 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7436 {
7437   PetscInt        dof;   /* The number of unknowns on this point */
7438   PetscInt        cdof;  /* The number of constraints on this point */
7439   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7440   PetscInt        cind = 0, k;
7441 
7442   PetscFunctionBegin;
7443   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7444   PetscCall(PetscSectionGetDof(section, point, &dof));
7445   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7446   if (!cdof || setBC) {
7447     for (k = 0; k < dof; ++k) {
7448       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7449       const PetscInt ind    = indperm ? indperm[preind] : preind;
7450 
7451       indices[ind] = off + k;
7452     }
7453   } else {
7454     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7455     for (k = 0; k < dof; ++k) {
7456       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7457       const PetscInt ind    = indperm ? indperm[preind] : preind;
7458 
7459       if ((cind < cdof) && (k == cdofs[cind])) {
7460         /* Insert check for returning constrained indices */
7461         indices[ind] = -(off + k + 1);
7462         ++cind;
7463       } else {
7464         indices[ind] = off + k - (islocal ? 0 : cind);
7465       }
7466     }
7467   }
7468   *loff += dof;
7469   PetscFunctionReturn(PETSC_SUCCESS);
7470 }
7471 
7472 /*
7473  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7474 
7475  Input Parameters:
7476 + section - a section (global or local)
7477 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7478 . point - point within section
7479 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7480 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7481 . setBC - identify constrained (boundary condition) points via involution.
7482 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7483 . permsoff - offset
7484 - indperm - index permutation
7485 
7486  Output Parameter:
7487 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7488 . indices - array to hold indices (as defined by section) of each dof associated with point
7489 
7490  Notes:
7491  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7492  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7493  in the local vector.
7494 
7495  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7496  significant).  It is invalid to call with a global section and setBC=true.
7497 
7498  Developer Note:
7499  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7500  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7501  offset could be obtained from the section instead of passing it explicitly as we do now.
7502 
7503  Example:
7504  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7505  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7506  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7507  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.
7508 
7509  Level: developer
7510 */
7511 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[])
7512 {
7513   PetscInt numFields, foff, f;
7514 
7515   PetscFunctionBegin;
7516   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7517   PetscCall(PetscSectionGetNumFields(section, &numFields));
7518   for (f = 0, foff = 0; f < numFields; ++f) {
7519     PetscInt        fdof, cfdof;
7520     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7521     PetscInt        cind = 0, b;
7522     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7523 
7524     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7525     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7526     if (!cfdof || setBC) {
7527       for (b = 0; b < fdof; ++b) {
7528         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7529         const PetscInt ind    = indperm ? indperm[preind] : preind;
7530 
7531         indices[ind] = off + foff + b;
7532       }
7533     } else {
7534       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7535       for (b = 0; b < fdof; ++b) {
7536         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7537         const PetscInt ind    = indperm ? indperm[preind] : preind;
7538 
7539         if ((cind < cfdof) && (b == fcdofs[cind])) {
7540           indices[ind] = -(off + foff + b + 1);
7541           ++cind;
7542         } else {
7543           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7544         }
7545       }
7546     }
7547     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7548     foffs[f] += fdof;
7549   }
7550   PetscFunctionReturn(PETSC_SUCCESS);
7551 }
7552 
7553 /*
7554   This version believes the globalSection offsets for each field, rather than just the point offset
7555 
7556  . foffs - The offset into 'indices' for each field, since it is segregated by field
7557 
7558  Notes:
7559  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7560  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7561 */
7562 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7563 {
7564   PetscInt numFields, foff, f;
7565 
7566   PetscFunctionBegin;
7567   PetscCall(PetscSectionGetNumFields(section, &numFields));
7568   for (f = 0; f < numFields; ++f) {
7569     PetscInt        fdof, cfdof;
7570     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7571     PetscInt        cind = 0, b;
7572     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7573 
7574     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7575     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7576     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7577     if (!cfdof) {
7578       for (b = 0; b < fdof; ++b) {
7579         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7580         const PetscInt ind    = indperm ? indperm[preind] : preind;
7581 
7582         indices[ind] = foff + b;
7583       }
7584     } else {
7585       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7586       for (b = 0; b < fdof; ++b) {
7587         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7588         const PetscInt ind    = indperm ? indperm[preind] : preind;
7589 
7590         if ((cind < cfdof) && (b == fcdofs[cind])) {
7591           indices[ind] = -(foff + b + 1);
7592           ++cind;
7593         } else {
7594           indices[ind] = foff + b - cind;
7595         }
7596       }
7597     }
7598     foffs[f] += fdof;
7599   }
7600   PetscFunctionReturn(PETSC_SUCCESS);
7601 }
7602 
7603 static PetscErrorCode DMPlexAnchorsGetSubMatIndices(PetscInt nPoints, const PetscInt pnts[], PetscSection section, PetscSection cSec, PetscInt tmpIndices[], PetscInt fieldOffsets[], PetscInt indices[], const PetscInt ***perms)
7604 {
7605   PetscInt numFields, sStart, sEnd, cStart, cEnd;
7606 
7607   PetscFunctionBegin;
7608   PetscCall(PetscSectionGetNumFields(section, &numFields));
7609   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7610   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7611   for (PetscInt p = 0; p < nPoints; p++) {
7612     PetscInt     b       = pnts[2 * p];
7613     PetscInt     bSecDof = 0, bOff;
7614     PetscInt     cSecDof = 0;
7615     PetscSection indices_section;
7616 
7617     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7618     if (!bSecDof) continue;
7619     if (b >= cStart && b < cEnd) PetscCall(PetscSectionGetDof(cSec, b, &cSecDof));
7620     indices_section = cSecDof > 0 ? cSec : section;
7621     if (numFields) {
7622       PetscInt fStart[32], fEnd[32];
7623 
7624       fStart[0] = 0;
7625       fEnd[0]   = 0;
7626       for (PetscInt f = 0; f < numFields; f++) {
7627         PetscInt fDof = 0;
7628 
7629         PetscCall(PetscSectionGetFieldDof(indices_section, b, f, &fDof));
7630         fStart[f + 1] = fStart[f] + fDof;
7631         fEnd[f + 1]   = fStart[f + 1];
7632       }
7633       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7634       // only apply permutations on one side
7635       PetscCall(DMPlexGetIndicesPointFields_Internal(indices_section, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, perms ? p : -1, NULL, tmpIndices));
7636       for (PetscInt f = 0; f < numFields; f++) {
7637         for (PetscInt i = fStart[f]; i < fEnd[f]; i++) { indices[fieldOffsets[f]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1); }
7638       }
7639     } else {
7640       PetscInt bEnd = 0;
7641 
7642       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7643       PetscCall(DMPlexGetIndicesPoint_Internal(indices_section, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, tmpIndices));
7644 
7645       for (PetscInt i = 0; i < bEnd; i++) indices[fieldOffsets[0]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1);
7646     }
7647   }
7648   PetscFunctionReturn(PETSC_SUCCESS);
7649 }
7650 
7651 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[])
7652 {
7653   Mat             cMat;
7654   PetscSection    aSec, cSec;
7655   IS              aIS;
7656   PetscInt        aStart = -1, aEnd = -1;
7657   PetscInt        sStart = -1, sEnd = -1;
7658   PetscInt        cStart = -1, cEnd = -1;
7659   const PetscInt *anchors;
7660   PetscInt        numFields, p;
7661   PetscInt        newNumPoints = 0, newNumIndices = 0;
7662   PetscInt       *newPoints, *indices, *newIndices, *tmpIndices, *tmpNewIndices;
7663   PetscInt        oldOffsets[32];
7664   PetscInt        newOffsets[32];
7665   PetscInt        oldOffsetsCopy[32];
7666   PetscInt        newOffsetsCopy[32];
7667   PetscScalar    *modMat         = NULL;
7668   PetscBool       anyConstrained = PETSC_FALSE;
7669 
7670   PetscFunctionBegin;
7671   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7672   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7673   PetscCall(PetscSectionGetNumFields(section, &numFields));
7674 
7675   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7676   /* if there are point-to-point constraints */
7677   if (aSec) {
7678     PetscCall(PetscArrayzero(newOffsets, 32));
7679     PetscCall(PetscArrayzero(oldOffsets, 32));
7680     PetscCall(ISGetIndices(aIS, &anchors));
7681     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7682     PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7683     /* figure out how many points are going to be in the new element matrix
7684      * (we allow double counting, because it's all just going to be summed
7685      * into the global matrix anyway) */
7686     for (p = 0; p < 2 * numPoints; p += 2) {
7687       PetscInt b    = points[p];
7688       PetscInt bDof = 0, bSecDof = 0;
7689 
7690       if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7691       if (!bSecDof) continue;
7692 
7693       for (PetscInt f = 0; f < numFields; f++) {
7694         PetscInt fDof = 0;
7695 
7696         PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7697         oldOffsets[f + 1] += fDof;
7698       }
7699       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7700       if (bDof) {
7701         /* this point is constrained */
7702         /* it is going to be replaced by its anchors */
7703         PetscInt bOff, q;
7704 
7705         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7706         for (q = 0; q < bDof; q++) {
7707           PetscInt a    = anchors[bOff + q];
7708           PetscInt aDof = 0;
7709 
7710           if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7711           if (aDof) {
7712             anyConstrained = PETSC_TRUE;
7713             newNumPoints += 1;
7714           }
7715           newNumIndices += aDof;
7716           for (PetscInt f = 0; f < numFields; ++f) {
7717             PetscInt fDof = 0;
7718 
7719             if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7720             newOffsets[f + 1] += fDof;
7721           }
7722         }
7723       } else {
7724         /* this point is not constrained */
7725         newNumPoints++;
7726         newNumIndices += bSecDof;
7727         for (PetscInt f = 0; f < numFields; ++f) {
7728           PetscInt fDof;
7729 
7730           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7731           newOffsets[f + 1] += fDof;
7732         }
7733       }
7734     }
7735   }
7736   if (!anyConstrained) {
7737     if (outNumPoints) *outNumPoints = 0;
7738     if (outNumIndices) *outNumIndices = 0;
7739     if (outPoints) *outPoints = NULL;
7740     if (outMat) *outMat = NULL;
7741     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7742     PetscFunctionReturn(PETSC_SUCCESS);
7743   }
7744 
7745   if (outNumPoints) *outNumPoints = newNumPoints;
7746   if (outNumIndices) *outNumIndices = newNumIndices;
7747 
7748   for (PetscInt f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7749   for (PetscInt f = 0; f < numFields; ++f) oldOffsets[f + 1] += oldOffsets[f];
7750 
7751   if (!outPoints && !outMat) {
7752     if (offsets) {
7753       for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7754     }
7755     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7756     PetscFunctionReturn(PETSC_SUCCESS);
7757   }
7758 
7759   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7760   PetscCheck(!numFields || oldOffsets[numFields] == numIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, oldOffsets[numFields], numIndices);
7761 
7762   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7763   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7764 
7765   /* output arrays */
7766   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7767   PetscCall(PetscArrayzero(newPoints, 2 * newNumPoints));
7768 
7769   // get the new Points
7770   for (PetscInt p = 0, newP = 0; p < numPoints; p++) {
7771     PetscInt b    = points[2 * p];
7772     PetscInt bDof = 0, bSecDof = 0, bOff;
7773 
7774     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7775     if (!bSecDof) continue;
7776     if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7777     if (bDof) {
7778       PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7779       for (PetscInt q = 0; q < bDof; q++) {
7780         PetscInt a = anchors[bOff + q], aDof = 0;
7781 
7782         if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7783         if (aDof) {
7784           newPoints[2 * newP]     = a;
7785           newPoints[2 * newP + 1] = 0; // orientations are accounted for in constructing the matrix, newly added points are in default orientation
7786           newP++;
7787         }
7788       }
7789     } else {
7790       newPoints[2 * newP]     = b;
7791       newPoints[2 * newP + 1] = points[2 * p + 1];
7792       newP++;
7793     }
7794   }
7795 
7796   if (outMat) {
7797     PetscScalar *tmpMat;
7798     PetscCall(PetscArraycpy(oldOffsetsCopy, oldOffsets, 32));
7799     PetscCall(PetscArraycpy(newOffsetsCopy, newOffsets, 32));
7800 
7801     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &indices));
7802     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7803     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7804     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7805 
7806     for (PetscInt i = 0; i < numIndices; i++) indices[i] = -1;
7807     for (PetscInt i = 0; i < newNumIndices; i++) newIndices[i] = -1;
7808 
7809     PetscCall(DMPlexAnchorsGetSubMatIndices(numPoints, points, section, cSec, tmpIndices, oldOffsetsCopy, indices, perms));
7810     PetscCall(DMPlexAnchorsGetSubMatIndices(newNumPoints, newPoints, section, section, tmpNewIndices, newOffsetsCopy, newIndices, NULL));
7811 
7812     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7813     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7814     PetscCall(PetscArrayzero(modMat, newNumIndices * numIndices));
7815     // for each field, insert the anchor modification into modMat
7816     for (PetscInt f = 0; f < PetscMax(1, numFields); f++) {
7817       PetscInt fStart    = oldOffsets[f];
7818       PetscInt fNewStart = newOffsets[f];
7819       for (PetscInt p = 0, newP = 0, o = fStart, oNew = fNewStart; p < numPoints; p++) {
7820         PetscInt b    = points[2 * p];
7821         PetscInt bDof = 0, bSecDof = 0, bOff;
7822 
7823         if (b >= sStart && b < sEnd) {
7824           if (numFields) {
7825             PetscCall(PetscSectionGetFieldDof(section, b, f, &bSecDof));
7826           } else {
7827             PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7828           }
7829         }
7830         if (!bSecDof) continue;
7831         if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7832         if (bDof) {
7833           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7834           for (PetscInt q = 0; q < bDof; q++, newP++) {
7835             PetscInt a = anchors[bOff + q], aDof = 0;
7836 
7837             if (a >= sStart && a < sEnd) {
7838               if (numFields) {
7839                 PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
7840               } else {
7841                 PetscCall(PetscSectionGetDof(section, a, &aDof));
7842               }
7843             }
7844             if (aDof) {
7845               PetscCall(MatGetValues(cMat, bSecDof, &indices[o], aDof, &newIndices[oNew], tmpMat));
7846               for (PetscInt d = 0; d < bSecDof; d++) {
7847                 for (PetscInt e = 0; e < aDof; e++) modMat[(o + d) * newNumIndices + oNew + e] = tmpMat[d * aDof + e];
7848               }
7849             }
7850             oNew += aDof;
7851           }
7852         } else {
7853           // Insert the identity matrix in this block
7854           for (PetscInt d = 0; d < bSecDof; d++) modMat[(o + d) * newNumIndices + oNew + d] = 1;
7855           oNew += bSecDof;
7856           newP++;
7857         }
7858         o += bSecDof;
7859       }
7860     }
7861 
7862     *outMat = modMat;
7863 
7864     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7865     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7866     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7867     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7868     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &indices));
7869   }
7870   PetscCall(ISRestoreIndices(aIS, &anchors));
7871 
7872   /* output */
7873   if (outPoints) {
7874     *outPoints = newPoints;
7875   } else {
7876     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7877   }
7878   for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7879   PetscFunctionReturn(PETSC_SUCCESS);
7880 }
7881 
7882 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)
7883 {
7884   PetscScalar *modMat        = NULL;
7885   PetscInt     newNumIndices = -1;
7886 
7887   PetscFunctionBegin;
7888   /* 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.
7889      modMat is that matrix C */
7890   PetscCall(DMPlexAnchorsGetSubMatModification(dm, section, numPoints, numIndices, points, perms, outNumPoints, &newNumIndices, outPoints, offsets, outValues ? &modMat : NULL));
7891   if (outNumIndices) *outNumIndices = newNumIndices;
7892   if (modMat) {
7893     const PetscScalar *newValues = values;
7894 
7895     if (multiplyRight) {
7896       PetscScalar *newNewValues = NULL;
7897       PetscBLASInt M, N, K;
7898       PetscScalar  a = 1.0, b = 0.0;
7899 
7900       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);
7901 
7902       PetscCall(PetscBLASIntCast(newNumIndices, &M));
7903       PetscCall(PetscBLASIntCast(numRows, &N));
7904       PetscCall(PetscBLASIntCast(numIndices, &K));
7905       PetscCall(DMGetWorkArray(dm, numRows * newNumIndices, MPIU_SCALAR, &newNewValues));
7906       // row-major to column-major conversion, right multiplication becomes left multiplication
7907       PetscCallBLAS("BLASgemm", BLASgemm_("N", "N", &M, &N, &K, &a, modMat, &M, newValues, &K, &b, newNewValues, &M));
7908       numCols   = newNumIndices;
7909       newValues = newNewValues;
7910     }
7911 
7912     if (multiplyLeft) {
7913       PetscScalar *newNewValues = NULL;
7914       PetscBLASInt M, N, K;
7915       PetscScalar  a = 1.0, b = 0.0;
7916 
7917       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);
7918 
7919       PetscCall(PetscBLASIntCast(numCols, &M));
7920       PetscCall(PetscBLASIntCast(newNumIndices, &N));
7921       PetscCall(PetscBLASIntCast(numIndices, &K));
7922       PetscCall(DMGetWorkArray(dm, newNumIndices * numCols, MPIU_SCALAR, &newNewValues));
7923       // row-major to column-major conversion, left multiplication becomes right multiplication
7924       PetscCallBLAS("BLASgemm", BLASgemm_("N", "T", &M, &N, &K, &a, newValues, &M, modMat, &N, &b, newNewValues, &M));
7925       if (newValues != values) PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &newValues));
7926       newValues = newNewValues;
7927     }
7928     *outValues = (PetscScalar *)newValues;
7929     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7930   }
7931   PetscFunctionReturn(PETSC_SUCCESS);
7932 }
7933 
7934 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)
7935 {
7936   PetscFunctionBegin;
7937   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, numPoints, numIndices, points, perms, numIndices, numIndices, values, outNumPoints, outNumIndices, outPoints, outValues, offsets, PETSC_TRUE, multiplyLeft));
7938   PetscFunctionReturn(PETSC_SUCCESS);
7939 }
7940 
7941 static PetscErrorCode DMPlexGetClosureIndicesSize_Internal(DM dm, PetscSection section, PetscInt point, PetscInt *closureSize)
7942 {
7943   /* Closure ordering */
7944   PetscSection    clSection;
7945   IS              clPoints;
7946   const PetscInt *clp;
7947   PetscInt       *points;
7948   PetscInt        Ncl, Ni = 0;
7949 
7950   PetscFunctionBeginHot;
7951   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7952   for (PetscInt p = 0; p < Ncl * 2; p += 2) {
7953     PetscInt dof;
7954 
7955     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7956     Ni += dof;
7957   }
7958   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7959   *closureSize = Ni;
7960   PetscFunctionReturn(PETSC_SUCCESS);
7961 }
7962 
7963 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)
7964 {
7965   /* Closure ordering */
7966   PetscSection    clSection;
7967   IS              clPoints;
7968   const PetscInt *clp;
7969   PetscInt       *points;
7970   const PetscInt *clperm = NULL;
7971   /* Dof permutation and sign flips */
7972   const PetscInt    **perms[32] = {NULL};
7973   const PetscScalar **flips[32] = {NULL};
7974   PetscScalar        *valCopy   = NULL;
7975   /* Hanging node constraints */
7976   PetscInt    *pointsC = NULL;
7977   PetscScalar *valuesC = NULL;
7978   PetscInt     NclC, NiC;
7979 
7980   PetscInt *idx;
7981   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7982   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7983   PetscInt  idxStart, idxEnd;
7984   PetscInt  nRows, nCols;
7985 
7986   PetscFunctionBeginHot;
7987   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7988   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7989   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7990   PetscAssertPointer(numRows, 6);
7991   PetscAssertPointer(numCols, 7);
7992   if (indices) PetscAssertPointer(indices, 8);
7993   if (outOffsets) PetscAssertPointer(outOffsets, 9);
7994   if (values) PetscAssertPointer(values, 10);
7995   PetscCall(PetscSectionGetNumFields(section, &Nf));
7996   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7997   PetscCall(PetscArrayzero(offsets, 32));
7998   /* 1) Get points in closure */
7999   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
8000   if (useClPerm) {
8001     PetscInt depth, clsize;
8002     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
8003     for (clsize = 0, p = 0; p < Ncl; p++) {
8004       PetscInt dof;
8005       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
8006       clsize += dof;
8007     }
8008     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
8009   }
8010   /* 2) Get number of indices on these points and field offsets from section */
8011   for (p = 0; p < Ncl * 2; p += 2) {
8012     PetscInt dof, fdof;
8013 
8014     PetscCall(PetscSectionGetDof(section, points[p], &dof));
8015     for (f = 0; f < Nf; ++f) {
8016       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
8017       offsets[f + 1] += fdof;
8018     }
8019     Ni += dof;
8020   }
8021   if (*numRows == -1) *numRows = Ni;
8022   if (*numCols == -1) *numCols = Ni;
8023   nRows = *numRows;
8024   nCols = *numCols;
8025   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
8026   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
8027   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
8028   if (multiplyRight) PetscCheck(nCols == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " columns, got %" PetscInt_FMT, Ni, nCols);
8029   if (multiplyLeft) PetscCheck(nRows == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " rows, got %" PetscInt_FMT, Ni, nRows);
8030   for (f = 0; f < PetscMax(1, Nf); ++f) {
8031     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8032     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
8033     /* may need to apply sign changes to the element matrix */
8034     if (values && flips[f]) {
8035       PetscInt foffset = offsets[f];
8036 
8037       for (p = 0; p < Ncl; ++p) {
8038         PetscInt           pnt  = points[2 * p], fdof;
8039         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
8040 
8041         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
8042         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
8043         if (flip) {
8044           PetscInt i, j, k;
8045 
8046           if (!valCopy) {
8047             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8048             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
8049             *values = valCopy;
8050           }
8051           for (i = 0; i < fdof; ++i) {
8052             PetscScalar fval = flip[i];
8053 
8054             if (multiplyRight) {
8055               for (k = 0; k < nRows; ++k) { valCopy[Ni * k + (foffset + i)] *= fval; }
8056             }
8057             if (multiplyLeft) {
8058               for (k = 0; k < nCols; ++k) { valCopy[nCols * (foffset + i) + k] *= fval; }
8059             }
8060           }
8061         }
8062         foffset += fdof;
8063       }
8064     }
8065   }
8066   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
8067   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, Ncl, Ni, points, perms, nRows, nCols, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, multiplyRight, multiplyLeft));
8068   if (NclC) {
8069     if (multiplyRight) *numCols = NiC;
8070     if (multiplyLeft) *numRows = NiC;
8071     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8072     for (f = 0; f < PetscMax(1, Nf); ++f) {
8073       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8074       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8075     }
8076     for (f = 0; f < PetscMax(1, Nf); ++f) {
8077       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
8078       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
8079     }
8080     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8081     Ncl    = NclC;
8082     Ni     = NiC;
8083     points = pointsC;
8084     if (values) *values = valuesC;
8085   }
8086   /* 5) Calculate indices */
8087   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
8088   PetscCall(PetscSectionGetChart(idxSection, &idxStart, &idxEnd));
8089   if (Nf) {
8090     PetscInt  idxOff;
8091     PetscBool useFieldOffsets;
8092 
8093     if (outOffsets) {
8094       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
8095     }
8096     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
8097     if (useFieldOffsets) {
8098       for (p = 0; p < Ncl; ++p) {
8099         const PetscInt pnt = points[p * 2];
8100 
8101         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
8102       }
8103     } else {
8104       for (p = 0; p < Ncl; ++p) {
8105         const PetscInt pnt = points[p * 2];
8106 
8107         if (pnt < idxStart || pnt >= idxEnd) continue;
8108         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8109         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8110          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
8111          * global section. */
8112         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
8113       }
8114     }
8115   } else {
8116     PetscInt off = 0, idxOff;
8117 
8118     for (p = 0; p < Ncl; ++p) {
8119       const PetscInt  pnt  = points[p * 2];
8120       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
8121 
8122       if (pnt < idxStart || pnt >= idxEnd) continue;
8123       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8124       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8125        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
8126       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
8127     }
8128   }
8129   /* 6) Cleanup */
8130   for (f = 0; f < PetscMax(1, Nf); ++f) {
8131     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8132     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8133   }
8134   if (NclC) {
8135     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
8136   } else {
8137     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8138   }
8139 
8140   if (indices) *indices = idx;
8141   PetscFunctionReturn(PETSC_SUCCESS);
8142 }
8143 
8144 /*@C
8145   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
8146 
8147   Not collective
8148 
8149   Input Parameters:
8150 + dm         - The `DM`
8151 . section    - The `PetscSection` describing the points (a local section)
8152 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8153 . point      - The point defining the closure
8154 - useClPerm  - Use the closure point permutation if available
8155 
8156   Output Parameters:
8157 + numIndices - The number of dof indices in the closure of point with the input sections
8158 . indices    - The dof indices
8159 . outOffsets - Array to write the field offsets into, or `NULL`
8160 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8161 
8162   Level: advanced
8163 
8164   Notes:
8165   Call `DMPlexRestoreClosureIndices()` to free allocated memory
8166 
8167   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8168   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
8169   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8170   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
8171   indices (with the above semantics) are implied.
8172 
8173 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
8174           `PetscSection`, `DMGetGlobalSection()`
8175 @*/
8176 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8177 {
8178   PetscInt numRows = -1, numCols = -1;
8179 
8180   PetscFunctionBeginHot;
8181   PetscCall(DMPlexGetClosureIndices_Internal(dm, section, idxSection, point, useClPerm, &numRows, &numCols, indices, outOffsets, values, PETSC_TRUE, PETSC_TRUE));
8182   PetscCheck(numRows == numCols, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Symmetric matrix transformation produces rectangular dimensions (%" PetscInt_FMT ", %" PetscInt_FMT ")", numRows, numCols);
8183   *numIndices = numRows;
8184   PetscFunctionReturn(PETSC_SUCCESS);
8185 }
8186 
8187 /*@C
8188   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
8189 
8190   Not collective
8191 
8192   Input Parameters:
8193 + dm         - The `DM`
8194 . section    - The `PetscSection` describing the points (a local section)
8195 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8196 . point      - The point defining the closure
8197 - useClPerm  - Use the closure point permutation if available
8198 
8199   Output Parameters:
8200 + numIndices - The number of dof indices in the closure of point with the input sections
8201 . indices    - The dof indices
8202 . outOffsets - Array to write the field offsets into, or `NULL`
8203 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8204 
8205   Level: advanced
8206 
8207   Notes:
8208   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
8209 
8210   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8211   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
8212   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8213   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
8214   indices (with the above semantics) are implied.
8215 
8216 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
8217 @*/
8218 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8219 {
8220   PetscFunctionBegin;
8221   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8222   PetscAssertPointer(indices, 7);
8223   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
8224   PetscFunctionReturn(PETSC_SUCCESS);
8225 }
8226 
8227 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8228 {
8229   DM_Plex           *mesh = (DM_Plex *)dm->data;
8230   PetscInt          *indices;
8231   PetscInt           numIndices;
8232   const PetscScalar *valuesOrig = values;
8233   PetscErrorCode     ierr;
8234 
8235   PetscFunctionBegin;
8236   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8237   if (!section) PetscCall(DMGetLocalSection(dm, &section));
8238   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8239   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
8240   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
8241   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
8242 
8243   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
8244 
8245   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
8246   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8247   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
8248   if (ierr) {
8249     PetscMPIInt rank;
8250 
8251     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8252     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8253     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
8254     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8255     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8256     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
8257   }
8258   if (mesh->printFEM > 1) {
8259     PetscInt i;
8260     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
8261     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
8262     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8263   }
8264 
8265   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8266   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8267   PetscFunctionReturn(PETSC_SUCCESS);
8268 }
8269 
8270 /*@C
8271   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
8272 
8273   Not collective
8274 
8275   Input Parameters:
8276 + dm            - The `DM`
8277 . section       - The section describing the layout in `v`, or `NULL` to use the default section
8278 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
8279 . A             - The matrix
8280 . point         - The point in the `DM`
8281 . values        - The array of values
8282 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8283 
8284   Level: intermediate
8285 
8286 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8287 @*/
8288 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8289 {
8290   PetscFunctionBegin;
8291   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
8292   PetscFunctionReturn(PETSC_SUCCESS);
8293 }
8294 
8295 /*@C
8296   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
8297 
8298   Not collective
8299 
8300   Input Parameters:
8301 + dmRow            - The `DM` for the row fields
8302 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
8303 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
8304 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
8305 . dmCol            - The `DM` for the column fields
8306 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
8307 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
8308 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
8309 . A                - The matrix
8310 . point            - The point in the `DM`
8311 . values           - The array of values
8312 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8313 
8314   Level: intermediate
8315 
8316 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8317 @*/
8318 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)
8319 {
8320   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
8321   PetscInt          *indicesRow, *indicesCol;
8322   PetscInt           numIndicesRow = -1, numIndicesCol = -1;
8323   const PetscScalar *valuesV0 = values, *valuesV1, *valuesV2;
8324 
8325   PetscErrorCode ierr;
8326 
8327   PetscFunctionBegin;
8328   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
8329   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
8330   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
8331   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
8332   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
8333   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
8334   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
8335   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
8336   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
8337   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
8338   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
8339 
8340   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmRow, sectionRow, point, &numIndicesRow));
8341   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmCol, sectionCol, point, &numIndicesCol));
8342   valuesV1 = valuesV0;
8343   PetscCall(DMPlexGetClosureIndices_Internal(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV1, PETSC_FALSE, PETSC_TRUE));
8344   valuesV2 = valuesV1;
8345   PetscCall(DMPlexGetClosureIndices_Internal(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesRow, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2, PETSC_TRUE, PETSC_FALSE));
8346 
8347   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2));
8348   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8349   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2, mode);
8350   if (ierr) {
8351     PetscMPIInt rank;
8352 
8353     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8354     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8355     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8356     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV2));
8357     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8358     if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8359     if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8360   }
8361 
8362   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2));
8363   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8364   if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8365   if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8366   PetscFunctionReturn(PETSC_SUCCESS);
8367 }
8368 
8369 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8370 {
8371   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8372   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8373   PetscInt       *cpoints = NULL;
8374   PetscInt       *findices, *cindices;
8375   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8376   PetscInt        foffsets[32], coffsets[32];
8377   DMPolytopeType  ct;
8378   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8379   PetscErrorCode  ierr;
8380 
8381   PetscFunctionBegin;
8382   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8383   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8384   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8385   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8386   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8387   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8388   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8389   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8390   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8391   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8392   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8393   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8394   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8395   PetscCall(PetscArrayzero(foffsets, 32));
8396   PetscCall(PetscArrayzero(coffsets, 32));
8397   /* Column indices */
8398   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8399   maxFPoints = numCPoints;
8400   /* Compress out points not in the section */
8401   /*   TODO: Squeeze out points with 0 dof as well */
8402   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8403   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8404     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8405       cpoints[q * 2]     = cpoints[p];
8406       cpoints[q * 2 + 1] = cpoints[p + 1];
8407       ++q;
8408     }
8409   }
8410   numCPoints = q;
8411   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8412     PetscInt fdof;
8413 
8414     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8415     if (!dof) continue;
8416     for (f = 0; f < numFields; ++f) {
8417       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8418       coffsets[f + 1] += fdof;
8419     }
8420     numCIndices += dof;
8421   }
8422   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8423   /* Row indices */
8424   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8425   {
8426     DMPlexTransform tr;
8427     DMPolytopeType *rct;
8428     PetscInt       *rsize, *rcone, *rornt, Nt;
8429 
8430     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8431     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8432     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8433     numSubcells = rsize[Nt - 1];
8434     PetscCall(DMPlexTransformDestroy(&tr));
8435   }
8436   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8437   for (r = 0, q = 0; r < numSubcells; ++r) {
8438     /* TODO Map from coarse to fine cells */
8439     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8440     /* Compress out points not in the section */
8441     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8442     for (p = 0; p < numFPoints * 2; p += 2) {
8443       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8444         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8445         if (!dof) continue;
8446         for (s = 0; s < q; ++s)
8447           if (fpoints[p] == ftotpoints[s * 2]) break;
8448         if (s < q) continue;
8449         ftotpoints[q * 2]     = fpoints[p];
8450         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8451         ++q;
8452       }
8453     }
8454     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8455   }
8456   numFPoints = q;
8457   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8458     PetscInt fdof;
8459 
8460     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8461     if (!dof) continue;
8462     for (f = 0; f < numFields; ++f) {
8463       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8464       foffsets[f + 1] += fdof;
8465     }
8466     numFIndices += dof;
8467   }
8468   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8469 
8470   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8471   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8472   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8473   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8474   if (numFields) {
8475     const PetscInt **permsF[32] = {NULL};
8476     const PetscInt **permsC[32] = {NULL};
8477 
8478     for (f = 0; f < numFields; f++) {
8479       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8480       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8481     }
8482     for (p = 0; p < numFPoints; p++) {
8483       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8484       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8485     }
8486     for (p = 0; p < numCPoints; p++) {
8487       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8488       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8489     }
8490     for (f = 0; f < numFields; f++) {
8491       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8492       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8493     }
8494   } else {
8495     const PetscInt **permsF = NULL;
8496     const PetscInt **permsC = NULL;
8497 
8498     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8499     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8500     for (p = 0, off = 0; p < numFPoints; p++) {
8501       const PetscInt *perm = permsF ? permsF[p] : NULL;
8502 
8503       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8504       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8505     }
8506     for (p = 0, off = 0; p < numCPoints; p++) {
8507       const PetscInt *perm = permsC ? permsC[p] : NULL;
8508 
8509       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8510       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8511     }
8512     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8513     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8514   }
8515   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8516   /* TODO: flips */
8517   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8518   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8519   if (ierr) {
8520     PetscMPIInt rank;
8521 
8522     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8523     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8524     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8525     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8526     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8527   }
8528   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8529   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8530   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8531   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8532   PetscFunctionReturn(PETSC_SUCCESS);
8533 }
8534 
8535 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8536 {
8537   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8538   PetscInt       *cpoints      = NULL;
8539   PetscInt        foffsets[32] = {0}, coffsets[32] = {0};
8540   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8541   DMPolytopeType  ct;
8542   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8543 
8544   PetscFunctionBegin;
8545   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8546   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8547   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8548   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8549   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8550   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8551   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8552   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8553   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8554   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8555   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8556   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8557   /* Column indices */
8558   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8559   maxFPoints = numCPoints;
8560   /* Compress out points not in the section */
8561   /*   TODO: Squeeze out points with 0 dof as well */
8562   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8563   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8564     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8565       cpoints[q * 2]     = cpoints[p];
8566       cpoints[q * 2 + 1] = cpoints[p + 1];
8567       ++q;
8568     }
8569   }
8570   numCPoints = q;
8571   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8572     PetscInt fdof;
8573 
8574     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8575     if (!dof) continue;
8576     for (f = 0; f < numFields; ++f) {
8577       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8578       coffsets[f + 1] += fdof;
8579     }
8580     numCIndices += dof;
8581   }
8582   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8583   /* Row indices */
8584   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8585   {
8586     DMPlexTransform tr;
8587     DMPolytopeType *rct;
8588     PetscInt       *rsize, *rcone, *rornt, Nt;
8589 
8590     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8591     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8592     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8593     numSubcells = rsize[Nt - 1];
8594     PetscCall(DMPlexTransformDestroy(&tr));
8595   }
8596   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8597   for (r = 0, q = 0; r < numSubcells; ++r) {
8598     /* TODO Map from coarse to fine cells */
8599     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8600     /* Compress out points not in the section */
8601     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8602     for (p = 0; p < numFPoints * 2; p += 2) {
8603       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8604         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8605         if (!dof) continue;
8606         for (s = 0; s < q; ++s)
8607           if (fpoints[p] == ftotpoints[s * 2]) break;
8608         if (s < q) continue;
8609         ftotpoints[q * 2]     = fpoints[p];
8610         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8611         ++q;
8612       }
8613     }
8614     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8615   }
8616   numFPoints = q;
8617   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8618     PetscInt fdof;
8619 
8620     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8621     if (!dof) continue;
8622     for (f = 0; f < numFields; ++f) {
8623       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8624       foffsets[f + 1] += fdof;
8625     }
8626     numFIndices += dof;
8627   }
8628   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8629 
8630   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8631   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8632   if (numFields) {
8633     const PetscInt **permsF[32] = {NULL};
8634     const PetscInt **permsC[32] = {NULL};
8635 
8636     for (f = 0; f < numFields; f++) {
8637       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8638       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8639     }
8640     for (p = 0; p < numFPoints; p++) {
8641       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8642       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8643     }
8644     for (p = 0; p < numCPoints; p++) {
8645       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8646       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8647     }
8648     for (f = 0; f < numFields; f++) {
8649       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8650       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8651     }
8652   } else {
8653     const PetscInt **permsF = NULL;
8654     const PetscInt **permsC = NULL;
8655 
8656     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8657     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8658     for (p = 0, off = 0; p < numFPoints; p++) {
8659       const PetscInt *perm = permsF ? permsF[p] : NULL;
8660 
8661       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8662       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8663     }
8664     for (p = 0, off = 0; p < numCPoints; p++) {
8665       const PetscInt *perm = permsC ? permsC[p] : NULL;
8666 
8667       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8668       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8669     }
8670     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8671     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8672   }
8673   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8674   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8675   PetscFunctionReturn(PETSC_SUCCESS);
8676 }
8677 
8678 /*@
8679   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8680 
8681   Input Parameter:
8682 . dm - The `DMPLEX` object
8683 
8684   Output Parameter:
8685 . cellHeight - The height of a cell
8686 
8687   Level: developer
8688 
8689 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8690 @*/
8691 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8692 {
8693   DM_Plex *mesh = (DM_Plex *)dm->data;
8694 
8695   PetscFunctionBegin;
8696   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8697   PetscAssertPointer(cellHeight, 2);
8698   *cellHeight = mesh->vtkCellHeight;
8699   PetscFunctionReturn(PETSC_SUCCESS);
8700 }
8701 
8702 /*@
8703   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8704 
8705   Input Parameters:
8706 + dm         - The `DMPLEX` object
8707 - cellHeight - The height of a cell
8708 
8709   Level: developer
8710 
8711 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8712 @*/
8713 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8714 {
8715   DM_Plex *mesh = (DM_Plex *)dm->data;
8716 
8717   PetscFunctionBegin;
8718   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8719   mesh->vtkCellHeight = cellHeight;
8720   PetscFunctionReturn(PETSC_SUCCESS);
8721 }
8722 
8723 /*@
8724   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8725 
8726   Input Parameters:
8727 + dm - The `DMPLEX` object
8728 - ct - The `DMPolytopeType` of the cell
8729 
8730   Output Parameters:
8731 + start - The first cell of this type, or `NULL`
8732 - end   - The upper bound on this celltype, or `NULL`
8733 
8734   Level: advanced
8735 
8736 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8737 @*/
8738 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PetscInt *start, PetscInt *end)
8739 {
8740   DM_Plex *mesh = (DM_Plex *)dm->data;
8741   DMLabel  label;
8742   PetscInt pStart, pEnd;
8743 
8744   PetscFunctionBegin;
8745   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8746   if (start) {
8747     PetscAssertPointer(start, 3);
8748     *start = 0;
8749   }
8750   if (end) {
8751     PetscAssertPointer(end, 4);
8752     *end = 0;
8753   }
8754   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8755   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8756   if (mesh->tr) {
8757     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8758   } else {
8759     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8760     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8761     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8762   }
8763   PetscFunctionReturn(PETSC_SUCCESS);
8764 }
8765 
8766 /*@
8767   DMPlexGetDepthStratumGlobalSize - Get the global size for a given depth stratum
8768 
8769   Input Parameters:
8770 + dm    - The `DMPLEX` object
8771 - depth - The depth for the given point stratum
8772 
8773   Output Parameter:
8774 . gsize - The global number of points in the stratum
8775 
8776   Level: advanced
8777 
8778 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8779 @*/
8780 PetscErrorCode DMPlexGetDepthStratumGlobalSize(DM dm, PetscInt depth, PetscInt *gsize)
8781 {
8782   PetscSF         sf;
8783   const PetscInt *leaves;
8784   PetscInt        Nl, loc, start, end, lsize = 0;
8785 
8786   PetscFunctionBegin;
8787   PetscCall(DMGetPointSF(dm, &sf));
8788   PetscCall(PetscSFGetGraph(sf, NULL, &Nl, &leaves, NULL));
8789   PetscCall(DMPlexGetDepthStratum(dm, depth, &start, &end));
8790   for (PetscInt p = start; p < end; ++p) {
8791     PetscCall(PetscFindInt(p, Nl, leaves, &loc));
8792     if (loc < 0) ++lsize;
8793   }
8794   PetscCallMPI(MPI_Allreduce(&lsize, gsize, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
8795   PetscFunctionReturn(PETSC_SUCCESS);
8796 }
8797 
8798 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8799 {
8800   PetscSection section, globalSection;
8801   PetscInt    *numbers, p;
8802 
8803   PetscFunctionBegin;
8804   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8805   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8806   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8807   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8808   PetscCall(PetscSectionSetUp(section));
8809   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &globalSection));
8810   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8811   for (p = pStart; p < pEnd; ++p) {
8812     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8813     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8814     else numbers[p - pStart] += shift;
8815   }
8816   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8817   if (globalSize) {
8818     PetscLayout layout;
8819     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8820     PetscCall(PetscLayoutGetSize(layout, globalSize));
8821     PetscCall(PetscLayoutDestroy(&layout));
8822   }
8823   PetscCall(PetscSectionDestroy(&section));
8824   PetscCall(PetscSectionDestroy(&globalSection));
8825   PetscFunctionReturn(PETSC_SUCCESS);
8826 }
8827 
8828 /*@
8829   DMPlexCreateCellNumbering - Get a global cell numbering for all cells on this process
8830 
8831   Input Parameters:
8832 + dm         - The `DMPLEX` object
8833 - includeAll - Whether to include all cells, or just the simplex and box cells
8834 
8835   Output Parameter:
8836 . globalCellNumbers - Global cell numbers for all cells on this process
8837 
8838   Level: developer
8839 
8840 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`
8841 @*/
8842 PetscErrorCode DMPlexCreateCellNumbering(DM dm, PetscBool includeAll, IS *globalCellNumbers)
8843 {
8844   PetscInt cellHeight, cStart, cEnd;
8845 
8846   PetscFunctionBegin;
8847   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8848   if (includeAll) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8849   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8850   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8851   PetscFunctionReturn(PETSC_SUCCESS);
8852 }
8853 
8854 /*@
8855   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8856 
8857   Input Parameter:
8858 . dm - The `DMPLEX` object
8859 
8860   Output Parameter:
8861 . globalCellNumbers - Global cell numbers for all cells on this process
8862 
8863   Level: developer
8864 
8865 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateCellNumbering()`, `DMPlexGetVertexNumbering()`
8866 @*/
8867 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8868 {
8869   DM_Plex *mesh = (DM_Plex *)dm->data;
8870 
8871   PetscFunctionBegin;
8872   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8873   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8874   *globalCellNumbers = mesh->globalCellNumbers;
8875   PetscFunctionReturn(PETSC_SUCCESS);
8876 }
8877 
8878 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8879 {
8880   PetscInt vStart, vEnd;
8881 
8882   PetscFunctionBegin;
8883   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8884   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8885   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8886   PetscFunctionReturn(PETSC_SUCCESS);
8887 }
8888 
8889 /*@
8890   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8891 
8892   Input Parameter:
8893 . dm - The `DMPLEX` object
8894 
8895   Output Parameter:
8896 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8897 
8898   Level: developer
8899 
8900 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8901 @*/
8902 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8903 {
8904   DM_Plex *mesh = (DM_Plex *)dm->data;
8905 
8906   PetscFunctionBegin;
8907   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8908   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8909   *globalVertexNumbers = mesh->globalVertexNumbers;
8910   PetscFunctionReturn(PETSC_SUCCESS);
8911 }
8912 
8913 /*@
8914   DMPlexCreatePointNumbering - Create a global numbering for all points.
8915 
8916   Collective
8917 
8918   Input Parameter:
8919 . dm - The `DMPLEX` object
8920 
8921   Output Parameter:
8922 . globalPointNumbers - Global numbers for all points on this process
8923 
8924   Level: developer
8925 
8926   Notes:
8927   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8928   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8929   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8930   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8931 
8932   The partitioned mesh is
8933   ```
8934   (2)--0--(3)--1--(4)    (1)--0--(2)
8935   ```
8936   and its global numbering is
8937   ```
8938   (3)--0--(4)--1--(5)--2--(6)
8939   ```
8940   Then the global numbering is provided as
8941   ```
8942   [0] Number of indices in set 5
8943   [0] 0 0
8944   [0] 1 1
8945   [0] 2 3
8946   [0] 3 4
8947   [0] 4 -6
8948   [1] Number of indices in set 3
8949   [1] 0 2
8950   [1] 1 5
8951   [1] 2 6
8952   ```
8953 
8954 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8955 @*/
8956 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8957 {
8958   IS        nums[4];
8959   PetscInt  depths[4], gdepths[4], starts[4];
8960   PetscInt  depth, d, shift = 0;
8961   PetscBool empty = PETSC_FALSE;
8962 
8963   PetscFunctionBegin;
8964   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8965   PetscCall(DMPlexGetDepth(dm, &depth));
8966   // For unstratified meshes use dim instead of depth
8967   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8968   // If any stratum is empty, we must mark all empty
8969   for (d = 0; d <= depth; ++d) {
8970     PetscInt end;
8971 
8972     depths[d] = depth - d;
8973     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8974     if (!(starts[d] - end)) empty = PETSC_TRUE;
8975   }
8976   if (empty)
8977     for (d = 0; d <= depth; ++d) {
8978       depths[d] = -1;
8979       starts[d] = -1;
8980     }
8981   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8982   PetscCallMPI(MPIU_Allreduce(depths, gdepths, (PetscMPIInt)(depth + 1), MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8983   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]);
8984   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8985   for (d = 0; d <= depth; ++d) {
8986     PetscInt pStart, pEnd, gsize;
8987 
8988     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8989     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8990     shift += gsize;
8991   }
8992   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
8993   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8994   PetscFunctionReturn(PETSC_SUCCESS);
8995 }
8996 
8997 /*@
8998   DMPlexCreateEdgeNumbering - Create a global numbering for edges.
8999 
9000   Collective
9001 
9002   Input Parameter:
9003 . dm - The `DMPLEX` object
9004 
9005   Output Parameter:
9006 . globalEdgeNumbers - Global numbers for all edges on this process
9007 
9008   Level: developer
9009 
9010   Notes:
9011   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).
9012 
9013 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`, `DMPlexCreatePointNumbering()`
9014 @*/
9015 PetscErrorCode DMPlexCreateEdgeNumbering(DM dm, IS *globalEdgeNumbers)
9016 {
9017   PetscSF  sf;
9018   PetscInt eStart, eEnd;
9019 
9020   PetscFunctionBegin;
9021   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9022   PetscCall(DMGetPointSF(dm, &sf));
9023   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9024   PetscCall(DMPlexCreateNumbering_Plex(dm, eStart, eEnd, 0, NULL, sf, globalEdgeNumbers));
9025   PetscFunctionReturn(PETSC_SUCCESS);
9026 }
9027 
9028 /*@
9029   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
9030 
9031   Input Parameter:
9032 . dm - The `DMPLEX` object
9033 
9034   Output Parameter:
9035 . ranks - The rank field
9036 
9037   Options Database Key:
9038 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
9039 
9040   Level: intermediate
9041 
9042 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
9043 @*/
9044 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
9045 {
9046   DM             rdm;
9047   PetscFE        fe;
9048   PetscScalar   *r;
9049   PetscMPIInt    rank;
9050   DMPolytopeType ct;
9051   PetscInt       dim, cStart, cEnd, c;
9052   PetscBool      simplex;
9053 
9054   PetscFunctionBeginUser;
9055   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9056   PetscAssertPointer(ranks, 2);
9057   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
9058   PetscCall(DMClone(dm, &rdm));
9059   PetscCall(DMGetDimension(rdm, &dim));
9060   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
9061   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
9062   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
9063   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
9064   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
9065   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9066   PetscCall(PetscFEDestroy(&fe));
9067   PetscCall(DMCreateDS(rdm));
9068   PetscCall(DMCreateGlobalVector(rdm, ranks));
9069   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
9070   PetscCall(VecGetArray(*ranks, &r));
9071   for (c = cStart; c < cEnd; ++c) {
9072     PetscScalar *lr;
9073 
9074     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
9075     if (lr) *lr = rank;
9076   }
9077   PetscCall(VecRestoreArray(*ranks, &r));
9078   PetscCall(DMDestroy(&rdm));
9079   PetscFunctionReturn(PETSC_SUCCESS);
9080 }
9081 
9082 /*@
9083   DMPlexCreateLabelField - Create a field whose value is the label value for that point
9084 
9085   Input Parameters:
9086 + dm    - The `DMPLEX`
9087 - label - The `DMLabel`
9088 
9089   Output Parameter:
9090 . val - The label value field
9091 
9092   Options Database Key:
9093 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
9094 
9095   Level: intermediate
9096 
9097 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
9098 @*/
9099 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
9100 {
9101   DM             rdm, plex;
9102   Vec            lval;
9103   PetscSection   section;
9104   PetscFE        fe;
9105   PetscScalar   *v;
9106   PetscInt       dim, pStart, pEnd, p, cStart;
9107   DMPolytopeType ct;
9108   char           name[PETSC_MAX_PATH_LEN];
9109   const char    *lname, *prefix;
9110 
9111   PetscFunctionBeginUser;
9112   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9113   PetscAssertPointer(label, 2);
9114   PetscAssertPointer(val, 3);
9115   PetscCall(DMClone(dm, &rdm));
9116   PetscCall(DMConvert(rdm, DMPLEX, &plex));
9117   PetscCall(DMPlexGetHeightStratum(plex, 0, &cStart, NULL));
9118   PetscCall(DMPlexGetCellType(plex, cStart, &ct));
9119   PetscCall(DMDestroy(&plex));
9120   PetscCall(DMGetDimension(rdm, &dim));
9121   PetscCall(DMGetOptionsPrefix(dm, &prefix));
9122   PetscCall(PetscObjectGetName((PetscObject)label, &lname));
9123   PetscCall(PetscSNPrintf(name, sizeof(name), "%s%s_", prefix ? prefix : "", lname));
9124   PetscCall(PetscFECreateByCell(PETSC_COMM_SELF, dim, 1, ct, name, -1, &fe));
9125   PetscCall(PetscObjectSetName((PetscObject)fe, ""));
9126   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9127   PetscCall(PetscFEDestroy(&fe));
9128   PetscCall(DMCreateDS(rdm));
9129   PetscCall(DMCreateGlobalVector(rdm, val));
9130   PetscCall(DMCreateLocalVector(rdm, &lval));
9131   PetscCall(PetscObjectSetName((PetscObject)*val, lname));
9132   PetscCall(DMGetLocalSection(rdm, &section));
9133   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
9134   PetscCall(VecGetArray(lval, &v));
9135   for (p = pStart; p < pEnd; ++p) {
9136     PetscInt cval, dof, off;
9137 
9138     PetscCall(PetscSectionGetDof(section, p, &dof));
9139     if (!dof) continue;
9140     PetscCall(DMLabelGetValue(label, p, &cval));
9141     PetscCall(PetscSectionGetOffset(section, p, &off));
9142     for (PetscInt d = 0; d < dof; d++) v[off + d] = cval;
9143   }
9144   PetscCall(VecRestoreArray(lval, &v));
9145   PetscCall(DMLocalToGlobal(rdm, lval, INSERT_VALUES, *val));
9146   PetscCall(VecDestroy(&lval));
9147   PetscCall(DMDestroy(&rdm));
9148   PetscFunctionReturn(PETSC_SUCCESS);
9149 }
9150 
9151 /*@
9152   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
9153 
9154   Input Parameter:
9155 . dm - The `DMPLEX` object
9156 
9157   Level: developer
9158 
9159   Notes:
9160   This is a useful diagnostic when creating meshes programmatically.
9161 
9162   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9163 
9164 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9165 @*/
9166 PetscErrorCode DMPlexCheckSymmetry(DM dm)
9167 {
9168   PetscSection    coneSection, supportSection;
9169   const PetscInt *cone, *support;
9170   PetscInt        coneSize, c, supportSize, s;
9171   PetscInt        pStart, pEnd, p, pp, csize, ssize;
9172   PetscBool       storagecheck = PETSC_TRUE;
9173 
9174   PetscFunctionBegin;
9175   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9176   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
9177   PetscCall(DMPlexGetConeSection(dm, &coneSection));
9178   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
9179   /* Check that point p is found in the support of its cone points, and vice versa */
9180   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9181   for (p = pStart; p < pEnd; ++p) {
9182     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
9183     PetscCall(DMPlexGetCone(dm, p, &cone));
9184     for (c = 0; c < coneSize; ++c) {
9185       PetscBool dup = PETSC_FALSE;
9186       PetscInt  d;
9187       for (d = c - 1; d >= 0; --d) {
9188         if (cone[c] == cone[d]) {
9189           dup = PETSC_TRUE;
9190           break;
9191         }
9192       }
9193       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
9194       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
9195       for (s = 0; s < supportSize; ++s) {
9196         if (support[s] == p) break;
9197       }
9198       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
9199         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
9200         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
9201         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9202         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
9203         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
9204         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9205         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]);
9206         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
9207       }
9208     }
9209     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
9210     if (p != pp) {
9211       storagecheck = PETSC_FALSE;
9212       continue;
9213     }
9214     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
9215     PetscCall(DMPlexGetSupport(dm, p, &support));
9216     for (s = 0; s < supportSize; ++s) {
9217       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
9218       PetscCall(DMPlexGetCone(dm, support[s], &cone));
9219       for (c = 0; c < coneSize; ++c) {
9220         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
9221         if (cone[c] != pp) {
9222           c = 0;
9223           break;
9224         }
9225         if (cone[c] == p) break;
9226       }
9227       if (c >= coneSize) {
9228         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
9229         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
9230         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9231         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
9232         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
9233         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9234         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
9235       }
9236     }
9237   }
9238   if (storagecheck) {
9239     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
9240     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
9241     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
9242   }
9243   PetscFunctionReturn(PETSC_SUCCESS);
9244 }
9245 
9246 /*
9247   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.
9248 */
9249 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
9250 {
9251   DMPolytopeType  cct;
9252   PetscInt        ptpoints[4];
9253   const PetscInt *cone, *ccone, *ptcone;
9254   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
9255 
9256   PetscFunctionBegin;
9257   *unsplit = 0;
9258   switch (ct) {
9259   case DM_POLYTOPE_POINT_PRISM_TENSOR:
9260     ptpoints[npt++] = c;
9261     break;
9262   case DM_POLYTOPE_SEG_PRISM_TENSOR:
9263     PetscCall(DMPlexGetCone(dm, c, &cone));
9264     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9265     for (cp = 0; cp < coneSize; ++cp) {
9266       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
9267       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
9268     }
9269     break;
9270   case DM_POLYTOPE_TRI_PRISM_TENSOR:
9271   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9272     PetscCall(DMPlexGetCone(dm, c, &cone));
9273     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9274     for (cp = 0; cp < coneSize; ++cp) {
9275       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
9276       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
9277       for (ccp = 0; ccp < cconeSize; ++ccp) {
9278         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
9279         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
9280           PetscInt p;
9281           for (p = 0; p < npt; ++p)
9282             if (ptpoints[p] == ccone[ccp]) break;
9283           if (p == npt) ptpoints[npt++] = ccone[ccp];
9284         }
9285       }
9286     }
9287     break;
9288   default:
9289     break;
9290   }
9291   for (pt = 0; pt < npt; ++pt) {
9292     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
9293     if (ptcone[0] == ptcone[1]) ++(*unsplit);
9294   }
9295   PetscFunctionReturn(PETSC_SUCCESS);
9296 }
9297 
9298 /*@
9299   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
9300 
9301   Input Parameters:
9302 + dm         - The `DMPLEX` object
9303 - cellHeight - Normally 0
9304 
9305   Level: developer
9306 
9307   Notes:
9308   This is a useful diagnostic when creating meshes programmatically.
9309   Currently applicable only to homogeneous simplex or tensor meshes.
9310 
9311   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9312 
9313 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9314 @*/
9315 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
9316 {
9317   DMPlexInterpolatedFlag interp;
9318   DMPolytopeType         ct;
9319   PetscInt               vStart, vEnd, cStart, cEnd, c;
9320 
9321   PetscFunctionBegin;
9322   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9323   PetscCall(DMPlexIsInterpolated(dm, &interp));
9324   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9325   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9326   for (c = cStart; c < cEnd; ++c) {
9327     PetscInt *closure = NULL;
9328     PetscInt  coneSize, closureSize, cl, Nv = 0;
9329 
9330     PetscCall(DMPlexGetCellType(dm, c, &ct));
9331     if (ct == DM_POLYTOPE_UNKNOWN) continue;
9332     if (interp == DMPLEX_INTERPOLATED_FULL) {
9333       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9334       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));
9335     }
9336     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9337     for (cl = 0; cl < closureSize * 2; cl += 2) {
9338       const PetscInt p = closure[cl];
9339       if ((p >= vStart) && (p < vEnd)) ++Nv;
9340     }
9341     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9342     /* Special Case: Tensor faces with identified vertices */
9343     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
9344       PetscInt unsplit;
9345 
9346       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9347       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
9348     }
9349     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));
9350   }
9351   PetscFunctionReturn(PETSC_SUCCESS);
9352 }
9353 
9354 /*@
9355   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
9356 
9357   Collective
9358 
9359   Input Parameters:
9360 + dm         - The `DMPLEX` object
9361 - cellHeight - Normally 0
9362 
9363   Level: developer
9364 
9365   Notes:
9366   This is a useful diagnostic when creating meshes programmatically.
9367   This routine is only relevant for meshes that are fully interpolated across all ranks.
9368   It will error out if a partially interpolated mesh is given on some rank.
9369   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
9370 
9371   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9372 
9373 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
9374 @*/
9375 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
9376 {
9377   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
9378   DMPlexInterpolatedFlag interpEnum;
9379 
9380   PetscFunctionBegin;
9381   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9382   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
9383   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
9384   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
9385     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
9386     PetscFunctionReturn(PETSC_SUCCESS);
9387   }
9388 
9389   PetscCall(DMGetDimension(dm, &dim));
9390   PetscCall(DMPlexGetDepth(dm, &depth));
9391   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9392   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
9393     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
9394     for (c = cStart; c < cEnd; ++c) {
9395       const PetscInt       *cone, *ornt, *faceSizes, *faces;
9396       const DMPolytopeType *faceTypes;
9397       DMPolytopeType        ct;
9398       PetscInt              numFaces, coneSize, f;
9399       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
9400 
9401       PetscCall(DMPlexGetCellType(dm, c, &ct));
9402       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9403       if (unsplit) continue;
9404       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9405       PetscCall(DMPlexGetCone(dm, c, &cone));
9406       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
9407       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9408       for (cl = 0; cl < closureSize * 2; cl += 2) {
9409         const PetscInt p = closure[cl];
9410         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
9411       }
9412       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9413       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);
9414       for (f = 0; f < numFaces; ++f) {
9415         DMPolytopeType fct;
9416         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
9417 
9418         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
9419         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
9420         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
9421           const PetscInt p = fclosure[cl];
9422           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
9423         }
9424         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]);
9425         for (v = 0; v < fnumCorners; ++v) {
9426           if (fclosure[v] != faces[fOff + v]) {
9427             PetscInt v1;
9428 
9429             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
9430             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
9431             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
9432             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
9433             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9434             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]);
9435           }
9436         }
9437         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
9438         fOff += faceSizes[f];
9439       }
9440       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9441       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9442     }
9443   }
9444   PetscFunctionReturn(PETSC_SUCCESS);
9445 }
9446 
9447 /*@
9448   DMPlexCheckGeometry - Check the geometry of mesh cells
9449 
9450   Input Parameter:
9451 . dm - The `DMPLEX` object
9452 
9453   Level: developer
9454 
9455   Notes:
9456   This is a useful diagnostic when creating meshes programmatically.
9457 
9458   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9459 
9460 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9461 @*/
9462 PetscErrorCode DMPlexCheckGeometry(DM dm)
9463 {
9464   Vec       coordinates;
9465   PetscReal detJ, J[9], refVol = 1.0;
9466   PetscReal vol;
9467   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9468 
9469   PetscFunctionBegin;
9470   PetscCall(DMGetDimension(dm, &dim));
9471   PetscCall(DMGetCoordinateDim(dm, &dE));
9472   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9473   PetscCall(DMPlexGetDepth(dm, &depth));
9474   for (d = 0; d < dim; ++d) refVol *= 2.0;
9475   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9476   /* Make sure local coordinates are created, because that step is collective */
9477   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9478   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9479   for (c = cStart; c < cEnd; ++c) {
9480     DMPolytopeType ct;
9481     PetscInt       unsplit;
9482     PetscBool      ignoreZeroVol = PETSC_FALSE;
9483 
9484     PetscCall(DMPlexGetCellType(dm, c, &ct));
9485     switch (ct) {
9486     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9487     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9488     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9489       ignoreZeroVol = PETSC_TRUE;
9490       break;
9491     default:
9492       break;
9493     }
9494     switch (ct) {
9495     case DM_POLYTOPE_TRI_PRISM:
9496     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9497     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9498     case DM_POLYTOPE_PYRAMID:
9499       continue;
9500     default:
9501       break;
9502     }
9503     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9504     if (unsplit) continue;
9505     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9506     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);
9507     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9508     /* This should work with periodicity since DG coordinates should be used */
9509     if (depth > 1) {
9510       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9511       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);
9512       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9513     }
9514   }
9515   PetscFunctionReturn(PETSC_SUCCESS);
9516 }
9517 
9518 /*@
9519   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9520 
9521   Collective
9522 
9523   Input Parameters:
9524 + dm              - The `DMPLEX` object
9525 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9526 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9527 
9528   Level: developer
9529 
9530   Notes:
9531   This is mainly intended for debugging/testing purposes.
9532 
9533   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9534 
9535   Extra roots can come from periodic cuts, where additional points appear on the boundary
9536 
9537 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9538 @*/
9539 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9540 {
9541   PetscInt           l, nleaves, nroots, overlap;
9542   const PetscInt    *locals;
9543   const PetscSFNode *remotes;
9544   PetscBool          distributed;
9545   MPI_Comm           comm;
9546   PetscMPIInt        rank;
9547 
9548   PetscFunctionBegin;
9549   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9550   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9551   else pointSF = dm->sf;
9552   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9553   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9554   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9555   {
9556     PetscMPIInt mpiFlag;
9557 
9558     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9559     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9560   }
9561   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9562   PetscCall(DMPlexIsDistributed(dm, &distributed));
9563   if (!distributed) {
9564     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);
9565     PetscFunctionReturn(PETSC_SUCCESS);
9566   }
9567   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);
9568   PetscCall(DMPlexGetOverlap(dm, &overlap));
9569 
9570   /* Check SF graph is compatible with DMPlex chart */
9571   {
9572     PetscInt pStart, pEnd, maxLeaf;
9573 
9574     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9575     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9576     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9577     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9578   }
9579 
9580   /* Check Point SF has no local points referenced */
9581   for (l = 0; l < nleaves; l++) {
9582     PetscAssert(remotes[l].rank != (PetscInt)rank, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains local point %" PetscInt_FMT " <- (%d,%" PetscInt_FMT ")", locals ? locals[l] : l, (PetscMPIInt)remotes[l].rank, remotes[l].index);
9583   }
9584 
9585   /* Check there are no cells in interface */
9586   if (!overlap) {
9587     PetscInt cellHeight, cStart, cEnd;
9588 
9589     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9590     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9591     for (l = 0; l < nleaves; ++l) {
9592       const PetscInt point = locals ? locals[l] : l;
9593 
9594       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9595     }
9596   }
9597 
9598   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9599   {
9600     const PetscInt *rootdegree;
9601 
9602     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9603     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9604     for (l = 0; l < nleaves; ++l) {
9605       const PetscInt  point = locals ? locals[l] : l;
9606       const PetscInt *cone;
9607       PetscInt        coneSize, c, idx;
9608 
9609       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9610       PetscCall(DMPlexGetCone(dm, point, &cone));
9611       for (c = 0; c < coneSize; ++c) {
9612         if (!rootdegree[cone[c]]) {
9613           if (locals) {
9614             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9615           } else {
9616             idx = (cone[c] < nleaves) ? cone[c] : -1;
9617           }
9618           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9619         }
9620       }
9621     }
9622   }
9623   PetscFunctionReturn(PETSC_SUCCESS);
9624 }
9625 
9626 /*@
9627   DMPlexCheckOrphanVertices - Check that no vertices are disconnected from the mesh, unless the mesh only consists of disconnected vertices.
9628 
9629   Collective
9630 
9631   Input Parameter:
9632 . dm - The `DMPLEX` object
9633 
9634   Level: developer
9635 
9636   Notes:
9637   This is mainly intended for debugging/testing purposes.
9638 
9639   Other cell types which are disconnected would be caught by the symmetry and face checks.
9640 
9641   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9642 
9643 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheck()`, `DMSetFromOptions()`
9644 @*/
9645 PetscErrorCode DMPlexCheckOrphanVertices(DM dm)
9646 {
9647   PetscInt pStart, pEnd, vStart, vEnd;
9648 
9649   PetscFunctionBegin;
9650   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9651   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9652   if (pStart == vStart && pEnd == vEnd) PetscFunctionReturn(PETSC_SUCCESS);
9653   for (PetscInt v = vStart; v < vEnd; ++v) {
9654     PetscInt suppSize;
9655 
9656     PetscCall(DMPlexGetSupportSize(dm, v, &suppSize));
9657     PetscCheck(suppSize, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Vertex %" PetscInt_FMT " is disconnected from the mesh", v);
9658   }
9659   PetscFunctionReturn(PETSC_SUCCESS);
9660 }
9661 
9662 /*@
9663   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9664 
9665   Input Parameter:
9666 . dm - The `DMPLEX` object
9667 
9668   Level: developer
9669 
9670   Notes:
9671   This is a useful diagnostic when creating meshes programmatically.
9672 
9673   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9674 
9675   Currently does not include `DMPlexCheckCellShape()`.
9676 
9677 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9678 @*/
9679 PetscErrorCode DMPlexCheck(DM dm)
9680 {
9681   PetscInt cellHeight;
9682 
9683   PetscFunctionBegin;
9684   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9685   PetscCall(DMPlexCheckSymmetry(dm));
9686   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9687   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9688   PetscCall(DMPlexCheckGeometry(dm));
9689   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9690   PetscCall(DMPlexCheckInterfaceCones(dm));
9691   PetscCall(DMPlexCheckOrphanVertices(dm));
9692   PetscFunctionReturn(PETSC_SUCCESS);
9693 }
9694 
9695 typedef struct cell_stats {
9696   PetscReal min, max, sum, squaresum;
9697   PetscInt  count;
9698 } cell_stats_t;
9699 
9700 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9701 {
9702   PetscInt i, N = *len;
9703 
9704   for (i = 0; i < N; i++) {
9705     cell_stats_t *A = (cell_stats_t *)a;
9706     cell_stats_t *B = (cell_stats_t *)b;
9707 
9708     B->min = PetscMin(A->min, B->min);
9709     B->max = PetscMax(A->max, B->max);
9710     B->sum += A->sum;
9711     B->squaresum += A->squaresum;
9712     B->count += A->count;
9713   }
9714 }
9715 
9716 /*@
9717   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9718 
9719   Collective
9720 
9721   Input Parameters:
9722 + dm        - The `DMPLEX` object
9723 . output    - If true, statistics will be displayed on `stdout`
9724 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9725 
9726   Level: developer
9727 
9728   Notes:
9729   This is mainly intended for debugging/testing purposes.
9730 
9731   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9732 
9733 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9734 @*/
9735 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9736 {
9737   DM           dmCoarse;
9738   cell_stats_t stats, globalStats;
9739   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9740   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9741   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9742   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9743   PetscMPIInt  rank, size;
9744 
9745   PetscFunctionBegin;
9746   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9747   stats.min = PETSC_MAX_REAL;
9748   stats.max = PETSC_MIN_REAL;
9749   stats.sum = stats.squaresum = 0.;
9750   stats.count                 = 0;
9751 
9752   PetscCallMPI(MPI_Comm_size(comm, &size));
9753   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9754   PetscCall(DMGetCoordinateDim(dm, &cdim));
9755   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9756   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9757   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9758   for (c = cStart; c < cEnd; c++) {
9759     PetscInt  i;
9760     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9761 
9762     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9763     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9764     for (i = 0; i < PetscSqr(cdim); ++i) {
9765       frobJ += J[i] * J[i];
9766       frobInvJ += invJ[i] * invJ[i];
9767     }
9768     cond2 = frobJ * frobInvJ;
9769     cond  = PetscSqrtReal(cond2);
9770 
9771     stats.min = PetscMin(stats.min, cond);
9772     stats.max = PetscMax(stats.max, cond);
9773     stats.sum += cond;
9774     stats.squaresum += cond2;
9775     stats.count++;
9776     if (output && cond > limit) {
9777       PetscSection coordSection;
9778       Vec          coordsLocal;
9779       PetscScalar *coords = NULL;
9780       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9781 
9782       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9783       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9784       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9785       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9786       for (i = 0; i < Nv / cdim; ++i) {
9787         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9788         for (d = 0; d < cdim; ++d) {
9789           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9790           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9791         }
9792         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9793       }
9794       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9795       for (cl = 0; cl < clSize * 2; cl += 2) {
9796         const PetscInt edge = closure[cl];
9797 
9798         if ((edge >= eStart) && (edge < eEnd)) {
9799           PetscReal len;
9800 
9801           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9802           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9803         }
9804       }
9805       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9806       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9807     }
9808   }
9809   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9810 
9811   if (size > 1) {
9812     PetscMPIInt  blockLengths[2] = {4, 1};
9813     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9814     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9815     MPI_Op       statReduce;
9816 
9817     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9818     PetscCallMPI(MPI_Type_commit(&statType));
9819     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9820     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9821     PetscCallMPI(MPI_Op_free(&statReduce));
9822     PetscCallMPI(MPI_Type_free(&statType));
9823   } else {
9824     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9825   }
9826   if (rank == 0) {
9827     count = globalStats.count;
9828     min   = globalStats.min;
9829     max   = globalStats.max;
9830     mean  = globalStats.sum / globalStats.count;
9831     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9832   }
9833 
9834   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));
9835   PetscCall(PetscFree2(J, invJ));
9836 
9837   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9838   if (dmCoarse) {
9839     PetscBool isplex;
9840 
9841     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9842     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9843   }
9844   PetscFunctionReturn(PETSC_SUCCESS);
9845 }
9846 
9847 /*@
9848   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9849   orthogonal quality below given tolerance.
9850 
9851   Collective
9852 
9853   Input Parameters:
9854 + dm   - The `DMPLEX` object
9855 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9856 - atol - [0, 1] Absolute tolerance for tagging cells.
9857 
9858   Output Parameters:
9859 + OrthQual      - `Vec` containing orthogonal quality per cell
9860 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9861 
9862   Options Database Keys:
9863 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9864 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9865 
9866   Level: intermediate
9867 
9868   Notes:
9869   Orthogonal quality is given by the following formula\:
9870 
9871   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9872 
9873   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
9874   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9875   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9876   calculating the cosine of the angle between these vectors.
9877 
9878   Orthogonal quality ranges from 1 (best) to 0 (worst).
9879 
9880   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9881   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9882 
9883   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9884 
9885 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9886 @*/
9887 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9888 {
9889   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9890   PetscInt              *idx;
9891   PetscScalar           *oqVals;
9892   const PetscScalar     *cellGeomArr, *faceGeomArr;
9893   PetscReal             *ci, *fi, *Ai;
9894   MPI_Comm               comm;
9895   Vec                    cellgeom, facegeom;
9896   DM                     dmFace, dmCell;
9897   IS                     glob;
9898   ISLocalToGlobalMapping ltog;
9899   PetscViewer            vwr;
9900 
9901   PetscFunctionBegin;
9902   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9903   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9904   PetscAssertPointer(OrthQual, 4);
9905   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9906   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9907   PetscCall(DMGetDimension(dm, &nc));
9908   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9909   {
9910     DMPlexInterpolatedFlag interpFlag;
9911 
9912     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9913     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9914       PetscMPIInt rank;
9915 
9916       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9917       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9918     }
9919   }
9920   if (OrthQualLabel) {
9921     PetscAssertPointer(OrthQualLabel, 5);
9922     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9923     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9924   } else {
9925     *OrthQualLabel = NULL;
9926   }
9927   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9928   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9929   PetscCall(DMPlexCreateCellNumbering(dm, PETSC_TRUE, &glob));
9930   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9931   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9932   PetscCall(VecCreate(comm, OrthQual));
9933   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9934   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9935   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9936   PetscCall(VecSetUp(*OrthQual));
9937   PetscCall(ISDestroy(&glob));
9938   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9939   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9940   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9941   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9942   PetscCall(VecGetDM(cellgeom, &dmCell));
9943   PetscCall(VecGetDM(facegeom, &dmFace));
9944   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9945   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9946     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9947     PetscInt         cellarr[2], *adj = NULL;
9948     PetscScalar     *cArr, *fArr;
9949     PetscReal        minvalc = 1.0, minvalf = 1.0;
9950     PetscFVCellGeom *cg;
9951 
9952     idx[cellIter] = cell - cStart;
9953     cellarr[0]    = cell;
9954     /* Make indexing into cellGeom easier */
9955     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9956     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9957     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9958     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9959     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9960       PetscInt         i;
9961       const PetscInt   neigh  = adj[cellneigh];
9962       PetscReal        normci = 0, normfi = 0, normai = 0;
9963       PetscFVCellGeom *cgneigh;
9964       PetscFVFaceGeom *fg;
9965 
9966       /* Don't count ourselves in the neighbor list */
9967       if (neigh == cell) continue;
9968       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9969       cellarr[1] = neigh;
9970       {
9971         PetscInt        numcovpts;
9972         const PetscInt *covpts;
9973 
9974         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9975         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9976         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9977       }
9978 
9979       /* Compute c_i, f_i and their norms */
9980       for (i = 0; i < nc; i++) {
9981         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9982         fi[i] = fg->centroid[i] - cg->centroid[i];
9983         Ai[i] = fg->normal[i];
9984         normci += PetscPowReal(ci[i], 2);
9985         normfi += PetscPowReal(fi[i], 2);
9986         normai += PetscPowReal(Ai[i], 2);
9987       }
9988       normci = PetscSqrtReal(normci);
9989       normfi = PetscSqrtReal(normfi);
9990       normai = PetscSqrtReal(normai);
9991 
9992       /* Normalize and compute for each face-cell-normal pair */
9993       for (i = 0; i < nc; i++) {
9994         ci[i] = ci[i] / normci;
9995         fi[i] = fi[i] / normfi;
9996         Ai[i] = Ai[i] / normai;
9997         /* PetscAbs because I don't know if normals are guaranteed to point out */
9998         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9999         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
10000       }
10001       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
10002       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
10003     }
10004     PetscCall(PetscFree(adj));
10005     PetscCall(PetscFree2(cArr, fArr));
10006     /* Defer to cell if they're equal */
10007     oqVals[cellIter] = PetscMin(minvalf, minvalc);
10008     if (OrthQualLabel) {
10009       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
10010     }
10011   }
10012   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
10013   PetscCall(VecAssemblyBegin(*OrthQual));
10014   PetscCall(VecAssemblyEnd(*OrthQual));
10015   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
10016   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
10017   PetscCall(PetscOptionsCreateViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
10018   if (OrthQualLabel) {
10019     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
10020   }
10021   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
10022   PetscCall(PetscViewerDestroy(&vwr));
10023   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
10024   PetscFunctionReturn(PETSC_SUCCESS);
10025 }
10026 
10027 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
10028  * interpolator construction */
10029 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
10030 {
10031   PetscSection section, newSection, gsection;
10032   PetscSF      sf;
10033   PetscBool    hasConstraints, ghasConstraints;
10034 
10035   PetscFunctionBegin;
10036   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10037   PetscAssertPointer(odm, 2);
10038   PetscCall(DMGetLocalSection(dm, &section));
10039   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
10040   PetscCallMPI(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
10041   if (!ghasConstraints) {
10042     PetscCall(PetscObjectReference((PetscObject)dm));
10043     *odm = dm;
10044     PetscFunctionReturn(PETSC_SUCCESS);
10045   }
10046   PetscCall(DMClone(dm, odm));
10047   PetscCall(DMCopyFields(dm, PETSC_DETERMINE, PETSC_DETERMINE, *odm));
10048   PetscCall(DMGetLocalSection(*odm, &newSection));
10049   PetscCall(DMGetPointSF(*odm, &sf));
10050   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_TRUE, PETSC_FALSE, &gsection));
10051   PetscCall(DMSetGlobalSection(*odm, gsection));
10052   PetscCall(PetscSectionDestroy(&gsection));
10053   PetscFunctionReturn(PETSC_SUCCESS);
10054 }
10055 
10056 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
10057 {
10058   DM        dmco, dmfo;
10059   Mat       interpo;
10060   Vec       rscale;
10061   Vec       cglobalo, clocal;
10062   Vec       fglobal, fglobalo, flocal;
10063   PetscBool regular;
10064 
10065   PetscFunctionBegin;
10066   PetscCall(DMGetFullDM(dmc, &dmco));
10067   PetscCall(DMGetFullDM(dmf, &dmfo));
10068   PetscCall(DMSetCoarseDM(dmfo, dmco));
10069   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
10070   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
10071   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
10072   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
10073   PetscCall(DMCreateLocalVector(dmc, &clocal));
10074   PetscCall(VecSet(cglobalo, 0.));
10075   PetscCall(VecSet(clocal, 0.));
10076   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
10077   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
10078   PetscCall(DMCreateLocalVector(dmf, &flocal));
10079   PetscCall(VecSet(fglobal, 0.));
10080   PetscCall(VecSet(fglobalo, 0.));
10081   PetscCall(VecSet(flocal, 0.));
10082   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
10083   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
10084   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
10085   PetscCall(MatMult(interpo, cglobalo, fglobalo));
10086   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
10087   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
10088   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
10089   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
10090   *shift = fglobal;
10091   PetscCall(VecDestroy(&flocal));
10092   PetscCall(VecDestroy(&fglobalo));
10093   PetscCall(VecDestroy(&clocal));
10094   PetscCall(VecDestroy(&cglobalo));
10095   PetscCall(VecDestroy(&rscale));
10096   PetscCall(MatDestroy(&interpo));
10097   PetscCall(DMDestroy(&dmfo));
10098   PetscCall(DMDestroy(&dmco));
10099   PetscFunctionReturn(PETSC_SUCCESS);
10100 }
10101 
10102 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
10103 {
10104   PetscObject shifto;
10105   Vec         shift;
10106 
10107   PetscFunctionBegin;
10108   if (!interp) {
10109     Vec rscale;
10110 
10111     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
10112     PetscCall(VecDestroy(&rscale));
10113   } else {
10114     PetscCall(PetscObjectReference((PetscObject)interp));
10115   }
10116   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
10117   if (!shifto) {
10118     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
10119     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
10120     shifto = (PetscObject)shift;
10121     PetscCall(VecDestroy(&shift));
10122   }
10123   shift = (Vec)shifto;
10124   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
10125   PetscCall(VecAXPY(fineSol, 1.0, shift));
10126   PetscCall(MatDestroy(&interp));
10127   PetscFunctionReturn(PETSC_SUCCESS);
10128 }
10129 
10130 /* Pointwise interpolation
10131      Just code FEM for now
10132      u^f = I u^c
10133      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
10134      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
10135      I_{ij} = psi^f_i phi^c_j
10136 */
10137 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
10138 {
10139   PetscSection gsc, gsf;
10140   PetscInt     m, n;
10141   void        *ctx;
10142   DM           cdm;
10143   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
10144 
10145   PetscFunctionBegin;
10146   PetscCall(DMGetGlobalSection(dmFine, &gsf));
10147   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10148   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10149   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10150 
10151   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
10152   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
10153   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10154   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
10155   PetscCall(DMGetApplicationContext(dmFine, &ctx));
10156 
10157   PetscCall(DMGetCoarseDM(dmFine, &cdm));
10158   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10159   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
10160   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
10161   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
10162   if (scaling) {
10163     /* Use naive scaling */
10164     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
10165   }
10166   PetscFunctionReturn(PETSC_SUCCESS);
10167 }
10168 
10169 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
10170 {
10171   VecScatter ctx;
10172 
10173   PetscFunctionBegin;
10174   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
10175   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
10176   PetscCall(VecScatterDestroy(&ctx));
10177   PetscFunctionReturn(PETSC_SUCCESS);
10178 }
10179 
10180 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[])
10181 {
10182   const PetscInt f  = (PetscInt)PetscRealPart(constants[numConstants]);
10183   const PetscInt Nc = uOff[f + 1] - uOff[f];
10184   for (PetscInt c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
10185 }
10186 
10187 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *lmass, Vec *mass)
10188 {
10189   DM           dmc;
10190   PetscDS      ds;
10191   Vec          ones, locmass;
10192   IS           cellIS;
10193   PetscFormKey key;
10194   PetscInt     depth;
10195 
10196   PetscFunctionBegin;
10197   PetscCall(DMClone(dm, &dmc));
10198   PetscCall(DMCopyDisc(dm, dmc));
10199   PetscCall(DMGetDS(dmc, &ds));
10200   for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10201   if (mass) PetscCall(DMCreateGlobalVector(dm, mass));
10202   if (lmass) PetscCall(DMCreateLocalVector(dm, &locmass));
10203   else PetscCall(DMGetLocalVector(dm, &locmass));
10204   PetscCall(DMGetLocalVector(dm, &ones));
10205   PetscCall(DMPlexGetDepth(dm, &depth));
10206   PetscCall(DMGetStratumIS(dm, "depth", depth, &cellIS));
10207   PetscCall(VecSet(locmass, 0.0));
10208   PetscCall(VecSet(ones, 1.0));
10209   key.label = NULL;
10210   key.value = 0;
10211   key.field = 0;
10212   key.part  = 0;
10213   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
10214   PetscCall(ISDestroy(&cellIS));
10215   if (mass) {
10216     PetscCall(DMLocalToGlobalBegin(dm, locmass, ADD_VALUES, *mass));
10217     PetscCall(DMLocalToGlobalEnd(dm, locmass, ADD_VALUES, *mass));
10218   }
10219   PetscCall(DMRestoreLocalVector(dm, &ones));
10220   if (lmass) *lmass = locmass;
10221   else PetscCall(DMRestoreLocalVector(dm, &locmass));
10222   PetscCall(DMDestroy(&dmc));
10223   PetscFunctionReturn(PETSC_SUCCESS);
10224 }
10225 
10226 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
10227 {
10228   PetscSection gsc, gsf;
10229   PetscInt     m, n;
10230   void        *ctx;
10231   DM           cdm;
10232   PetscBool    regular;
10233 
10234   PetscFunctionBegin;
10235   if (dmFine == dmCoarse) {
10236     DM            dmc;
10237     PetscDS       ds;
10238     PetscWeakForm wf;
10239     Vec           u;
10240     IS            cellIS;
10241     PetscFormKey  key;
10242     PetscInt      depth;
10243 
10244     PetscCall(DMClone(dmFine, &dmc));
10245     PetscCall(DMCopyDisc(dmFine, dmc));
10246     PetscCall(DMGetDS(dmc, &ds));
10247     PetscCall(PetscDSGetWeakForm(ds, &wf));
10248     PetscCall(PetscWeakFormClear(wf));
10249     for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10250     PetscCall(DMCreateMatrix(dmc, mass));
10251     PetscCall(DMGetLocalVector(dmc, &u));
10252     PetscCall(DMPlexGetDepth(dmc, &depth));
10253     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
10254     PetscCall(MatZeroEntries(*mass));
10255     key.label = NULL;
10256     key.value = 0;
10257     key.field = 0;
10258     key.part  = 0;
10259     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
10260     PetscCall(ISDestroy(&cellIS));
10261     PetscCall(DMRestoreLocalVector(dmc, &u));
10262     PetscCall(DMDestroy(&dmc));
10263   } else {
10264     PetscCall(DMGetGlobalSection(dmFine, &gsf));
10265     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10266     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10267     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10268 
10269     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
10270     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10271     PetscCall(MatSetType(*mass, dmCoarse->mattype));
10272     PetscCall(DMGetApplicationContext(dmFine, &ctx));
10273 
10274     PetscCall(DMGetCoarseDM(dmFine, &cdm));
10275     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10276     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
10277     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
10278   }
10279   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
10280   PetscFunctionReturn(PETSC_SUCCESS);
10281 }
10282 
10283 /*@
10284   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10285 
10286   Input Parameter:
10287 . dm - The `DMPLEX` object
10288 
10289   Output Parameter:
10290 . regular - The flag
10291 
10292   Level: intermediate
10293 
10294 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
10295 @*/
10296 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
10297 {
10298   PetscFunctionBegin;
10299   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10300   PetscAssertPointer(regular, 2);
10301   *regular = ((DM_Plex *)dm->data)->regularRefinement;
10302   PetscFunctionReturn(PETSC_SUCCESS);
10303 }
10304 
10305 /*@
10306   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10307 
10308   Input Parameters:
10309 + dm      - The `DMPLEX` object
10310 - regular - The flag
10311 
10312   Level: intermediate
10313 
10314 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
10315 @*/
10316 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
10317 {
10318   PetscFunctionBegin;
10319   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10320   ((DM_Plex *)dm->data)->regularRefinement = regular;
10321   PetscFunctionReturn(PETSC_SUCCESS);
10322 }
10323 
10324 /*@
10325   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
10326   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
10327 
10328   Not Collective
10329 
10330   Input Parameter:
10331 . dm - The `DMPLEX` object
10332 
10333   Output Parameters:
10334 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
10335 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
10336 
10337   Level: intermediate
10338 
10339 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
10340 @*/
10341 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
10342 {
10343   DM_Plex *plex = (DM_Plex *)dm->data;
10344 
10345   PetscFunctionBegin;
10346   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10347   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
10348   if (anchorSection) *anchorSection = plex->anchorSection;
10349   if (anchorIS) *anchorIS = plex->anchorIS;
10350   PetscFunctionReturn(PETSC_SUCCESS);
10351 }
10352 
10353 /*@
10354   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
10355 
10356   Collective
10357 
10358   Input Parameters:
10359 + dm            - The `DMPLEX` object
10360 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
10361                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10362 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10363 
10364   Level: intermediate
10365 
10366   Notes:
10367   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
10368   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
10369   combination of other points' degrees of freedom.
10370 
10371   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
10372   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
10373 
10374   The reference counts of `anchorSection` and `anchorIS` are incremented.
10375 
10376 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
10377 @*/
10378 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
10379 {
10380   DM_Plex    *plex = (DM_Plex *)dm->data;
10381   PetscMPIInt result;
10382 
10383   PetscFunctionBegin;
10384   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10385   if (anchorSection) {
10386     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
10387     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
10388     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
10389   }
10390   if (anchorIS) {
10391     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
10392     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
10393     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
10394   }
10395 
10396   PetscCall(PetscObjectReference((PetscObject)anchorSection));
10397   PetscCall(PetscSectionDestroy(&plex->anchorSection));
10398   plex->anchorSection = anchorSection;
10399 
10400   PetscCall(PetscObjectReference((PetscObject)anchorIS));
10401   PetscCall(ISDestroy(&plex->anchorIS));
10402   plex->anchorIS = anchorIS;
10403 
10404   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
10405     PetscInt        size, a, pStart, pEnd;
10406     const PetscInt *anchors;
10407 
10408     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10409     PetscCall(ISGetLocalSize(anchorIS, &size));
10410     PetscCall(ISGetIndices(anchorIS, &anchors));
10411     for (a = 0; a < size; a++) {
10412       PetscInt p;
10413 
10414       p = anchors[a];
10415       if (p >= pStart && p < pEnd) {
10416         PetscInt dof;
10417 
10418         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10419         if (dof) {
10420           PetscCall(ISRestoreIndices(anchorIS, &anchors));
10421           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
10422         }
10423       }
10424     }
10425     PetscCall(ISRestoreIndices(anchorIS, &anchors));
10426   }
10427   /* reset the generic constraints */
10428   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
10429   PetscFunctionReturn(PETSC_SUCCESS);
10430 }
10431 
10432 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
10433 {
10434   PetscSection anchorSection;
10435   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
10436 
10437   PetscFunctionBegin;
10438   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10439   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10440   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
10441   PetscCall(PetscSectionGetNumFields(section, &numFields));
10442   if (numFields) {
10443     PetscInt f;
10444     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
10445 
10446     for (f = 0; f < numFields; f++) {
10447       PetscInt numComp;
10448 
10449       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
10450       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
10451     }
10452   }
10453   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10454   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10455   pStart = PetscMax(pStart, sStart);
10456   pEnd   = PetscMin(pEnd, sEnd);
10457   pEnd   = PetscMax(pStart, pEnd);
10458   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
10459   for (p = pStart; p < pEnd; p++) {
10460     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10461     if (dof) {
10462       PetscCall(PetscSectionGetDof(section, p, &dof));
10463       PetscCall(PetscSectionSetDof(*cSec, p, dof));
10464       for (f = 0; f < numFields; f++) {
10465         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
10466         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
10467       }
10468     }
10469   }
10470   PetscCall(PetscSectionSetUp(*cSec));
10471   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
10472   PetscFunctionReturn(PETSC_SUCCESS);
10473 }
10474 
10475 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
10476 {
10477   PetscSection    aSec;
10478   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
10479   const PetscInt *anchors;
10480   PetscInt        numFields, f;
10481   IS              aIS;
10482   MatType         mtype;
10483   PetscBool       iscuda, iskokkos;
10484 
10485   PetscFunctionBegin;
10486   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10487   PetscCall(PetscSectionGetStorageSize(cSec, &m));
10488   PetscCall(PetscSectionGetStorageSize(section, &n));
10489   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
10490   PetscCall(MatSetSizes(*cMat, m, n, m, n));
10491   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
10492   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
10493   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
10494   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
10495   if (iscuda) mtype = MATSEQAIJCUSPARSE;
10496   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
10497   else mtype = MATSEQAIJ;
10498   PetscCall(MatSetType(*cMat, mtype));
10499   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
10500   PetscCall(ISGetIndices(aIS, &anchors));
10501   /* cSec will be a subset of aSec and section */
10502   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
10503   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10504   PetscCall(PetscMalloc1(m + 1, &i));
10505   i[0] = 0;
10506   PetscCall(PetscSectionGetNumFields(section, &numFields));
10507   for (p = pStart; p < pEnd; p++) {
10508     PetscInt rDof, rOff, r;
10509 
10510     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10511     if (!rDof) continue;
10512     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10513     if (numFields) {
10514       for (f = 0; f < numFields; f++) {
10515         annz = 0;
10516         for (r = 0; r < rDof; r++) {
10517           a = anchors[rOff + r];
10518           if (a < sStart || a >= sEnd) continue;
10519           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10520           annz += aDof;
10521         }
10522         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10523         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10524         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10525       }
10526     } else {
10527       annz = 0;
10528       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10529       for (q = 0; q < dof; q++) {
10530         a = anchors[rOff + q];
10531         if (a < sStart || a >= sEnd) continue;
10532         PetscCall(PetscSectionGetDof(section, a, &aDof));
10533         annz += aDof;
10534       }
10535       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10536       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10537       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10538     }
10539   }
10540   nnz = i[m];
10541   PetscCall(PetscMalloc1(nnz, &j));
10542   offset = 0;
10543   for (p = pStart; p < pEnd; p++) {
10544     if (numFields) {
10545       for (f = 0; f < numFields; f++) {
10546         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10547         for (q = 0; q < dof; q++) {
10548           PetscInt rDof, rOff, r;
10549           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10550           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10551           for (r = 0; r < rDof; r++) {
10552             PetscInt s;
10553 
10554             a = anchors[rOff + r];
10555             if (a < sStart || a >= sEnd) continue;
10556             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10557             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10558             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10559           }
10560         }
10561       }
10562     } else {
10563       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10564       for (q = 0; q < dof; q++) {
10565         PetscInt rDof, rOff, r;
10566         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10567         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10568         for (r = 0; r < rDof; r++) {
10569           PetscInt s;
10570 
10571           a = anchors[rOff + r];
10572           if (a < sStart || a >= sEnd) continue;
10573           PetscCall(PetscSectionGetDof(section, a, &aDof));
10574           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10575           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10576         }
10577       }
10578     }
10579   }
10580   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10581   PetscCall(PetscFree(i));
10582   PetscCall(PetscFree(j));
10583   PetscCall(ISRestoreIndices(aIS, &anchors));
10584   PetscFunctionReturn(PETSC_SUCCESS);
10585 }
10586 
10587 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10588 {
10589   DM_Plex     *plex = (DM_Plex *)dm->data;
10590   PetscSection anchorSection, section, cSec;
10591   Mat          cMat;
10592 
10593   PetscFunctionBegin;
10594   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10595   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10596   if (anchorSection) {
10597     PetscInt Nf;
10598 
10599     PetscCall(DMGetLocalSection(dm, &section));
10600     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10601     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10602     PetscCall(DMGetNumFields(dm, &Nf));
10603     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10604     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10605     PetscCall(PetscSectionDestroy(&cSec));
10606     PetscCall(MatDestroy(&cMat));
10607   }
10608   PetscFunctionReturn(PETSC_SUCCESS);
10609 }
10610 
10611 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10612 {
10613   IS           subis;
10614   PetscSection section, subsection;
10615 
10616   PetscFunctionBegin;
10617   PetscCall(DMGetLocalSection(dm, &section));
10618   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10619   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10620   /* Create subdomain */
10621   PetscCall(DMPlexFilter(dm, label, value, PETSC_FALSE, PETSC_FALSE, NULL, subdm));
10622   /* Create submodel */
10623   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10624   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10625   PetscCall(DMSetLocalSection(*subdm, subsection));
10626   PetscCall(PetscSectionDestroy(&subsection));
10627   PetscCall(DMCopyDisc(dm, *subdm));
10628   /* Create map from submodel to global model */
10629   if (is) {
10630     PetscSection    sectionGlobal, subsectionGlobal;
10631     IS              spIS;
10632     const PetscInt *spmap;
10633     PetscInt       *subIndices;
10634     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10635     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10636 
10637     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10638     PetscCall(ISGetIndices(spIS, &spmap));
10639     PetscCall(PetscSectionGetNumFields(section, &Nf));
10640     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10641     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10642     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10643     for (p = pStart; p < pEnd; ++p) {
10644       PetscInt gdof, pSubSize = 0;
10645 
10646       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10647       if (gdof > 0) {
10648         for (f = 0; f < Nf; ++f) {
10649           PetscInt fdof, fcdof;
10650 
10651           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10652           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10653           pSubSize += fdof - fcdof;
10654         }
10655         subSize += pSubSize;
10656         if (pSubSize) {
10657           if (bs < 0) {
10658             bs = pSubSize;
10659           } else if (bs != pSubSize) {
10660             /* Layout does not admit a pointwise block size */
10661             bs = 1;
10662           }
10663         }
10664       }
10665     }
10666     /* Must have same blocksize on all procs (some might have no points) */
10667     bsLocal[0] = bs < 0 ? PETSC_INT_MAX : bs;
10668     bsLocal[1] = bs;
10669     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10670     if (bsMinMax[0] != bsMinMax[1]) {
10671       bs = 1;
10672     } else {
10673       bs = bsMinMax[0];
10674     }
10675     PetscCall(PetscMalloc1(subSize, &subIndices));
10676     for (p = pStart; p < pEnd; ++p) {
10677       PetscInt gdof, goff;
10678 
10679       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10680       if (gdof > 0) {
10681         const PetscInt point = spmap[p];
10682 
10683         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10684         for (f = 0; f < Nf; ++f) {
10685           PetscInt fdof, fcdof, fc, f2, poff = 0;
10686 
10687           /* Can get rid of this loop by storing field information in the global section */
10688           for (f2 = 0; f2 < f; ++f2) {
10689             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10690             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10691             poff += fdof - fcdof;
10692           }
10693           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10694           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10695           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10696         }
10697       }
10698     }
10699     PetscCall(ISRestoreIndices(spIS, &spmap));
10700     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10701     if (bs > 1) {
10702       /* We need to check that the block size does not come from non-contiguous fields */
10703       PetscInt i, j, set = 1;
10704       for (i = 0; i < subSize; i += bs) {
10705         for (j = 0; j < bs; ++j) {
10706           if (subIndices[i + j] != subIndices[i] + j) {
10707             set = 0;
10708             break;
10709           }
10710         }
10711       }
10712       if (set) PetscCall(ISSetBlockSize(*is, bs));
10713     }
10714     /* Attach nullspace */
10715     for (f = 0; f < Nf; ++f) {
10716       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10717       if ((*subdm)->nullspaceConstructors[f]) break;
10718     }
10719     if (f < Nf) {
10720       MatNullSpace nullSpace;
10721       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10722 
10723       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10724       PetscCall(MatNullSpaceDestroy(&nullSpace));
10725     }
10726   }
10727   PetscFunctionReturn(PETSC_SUCCESS);
10728 }
10729 
10730 /*@
10731   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10732 
10733   Input Parameters:
10734 + dm    - The `DM`
10735 - dummy - unused argument
10736 
10737   Options Database Key:
10738 . -dm_plex_monitor_throughput - Activate the monitor
10739 
10740   Level: developer
10741 
10742 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10743 @*/
10744 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10745 {
10746   PetscLogHandler default_handler;
10747 
10748   PetscFunctionBegin;
10749   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10750   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10751   if (default_handler) {
10752     PetscLogEvent      event;
10753     PetscEventPerfInfo eventInfo;
10754     PetscReal          cellRate, flopRate;
10755     PetscInt           cStart, cEnd, Nf, N;
10756     const char        *name;
10757 
10758     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10759     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10760     PetscCall(DMGetNumFields(dm, &Nf));
10761     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10762     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10763     N        = (cEnd - cStart) * Nf * eventInfo.count;
10764     flopRate = eventInfo.flops / eventInfo.time;
10765     cellRate = N / eventInfo.time;
10766     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, (double)cellRate, (double)(flopRate / 1.e6)));
10767   } else {
10768     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.");
10769   }
10770   PetscFunctionReturn(PETSC_SUCCESS);
10771 }
10772