xref: /petsc/src/dm/impls/plex/plex.c (revision 75155f4810bbdad9dc5d1c7fa5006c4f40d2d440)
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(dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
252     } else {
253       PetscCall(PetscInfo(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   DM                 cdm;
277   PetscDS            ds;
278   PetscDraw          draw = NULL;
279   PetscDrawLG        lg;
280   Vec                coordinates;
281   const PetscScalar *coords, **sol;
282   PetscReal         *vals;
283   PetscInt          *Nc;
284   PetscInt           Nf, Nl, vStart, vEnd, eStart, eEnd;
285   char             **names;
286 
287   PetscFunctionBegin;
288   PetscCall(DMGetCoordinateDM(dm, &cdm));
289   PetscCall(DMGetDS(dm, &ds));
290   PetscCall(PetscDSGetNumFields(ds, &Nf));
291   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
292   PetscCall(PetscDSGetComponents(ds, &Nc));
293 
294   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
295   if (!draw) PetscFunctionReturn(PETSC_SUCCESS);
296   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
297 
298   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
299   for (PetscInt i = 0, l = 0; i < n; ++i) {
300     const char *vname;
301 
302     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
303     for (PetscInt f = 0; f < Nf; ++f) {
304       PetscObject disc;
305       const char *fname;
306       char        tmpname[PETSC_MAX_PATH_LEN];
307 
308       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
309       /* TODO Create names for components */
310       for (PetscInt c = 0; c < Nc[f]; ++c, ++l) {
311         PetscCall(PetscObjectGetName(disc, &fname));
312         PetscCall(PetscStrncpy(tmpname, vname, sizeof(tmpname)));
313         PetscCall(PetscStrlcat(tmpname, ":", sizeof(tmpname)));
314         PetscCall(PetscStrlcat(tmpname, fname, sizeof(tmpname)));
315         PetscCall(PetscStrallocpy(tmpname, &names[l]));
316       }
317     }
318   }
319   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
320   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
321   PetscCall(VecGetArrayRead(coordinates, &coords));
322   for (PetscInt i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
323   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
324   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
325   PetscSection s;
326   PetscInt     dof;
327 
328   PetscCall(DMGetLocalSection(dm, &s));
329   PetscCall(PetscSectionGetDof(s, eStart, &dof));
330   if (dof) {
331     // P_2
332     PetscInt vFirst = -1;
333 
334     for (PetscInt e = eStart; e < eEnd; ++e) {
335       PetscScalar    *xa, *xb, *svals;
336       const PetscInt *cone;
337 
338       PetscCall(DMPlexGetCone(dm, e, &cone));
339       PetscCall(DMPlexPointLocalRead(cdm, cone[0], coords, &xa));
340       PetscCall(DMPlexPointLocalRead(cdm, cone[1], coords, &xb));
341       if (e == eStart) vFirst = cone[0];
342       for (PetscInt i = 0; i < n; ++i) {
343         PetscCall(DMPlexPointLocalRead(dm, cone[0], sol[i], &svals));
344         for (PetscInt l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
345       }
346       PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(xa[0]), vals));
347       if (e == eEnd - 1 && cone[1] != vFirst) {
348         for (PetscInt i = 0; i < n; ++i) {
349           PetscCall(DMPlexPointLocalRead(dm, e, sol[i], &svals));
350           for (PetscInt l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
351         }
352         PetscCall(PetscDrawLGAddCommonPoint(lg, 0.5 * (PetscRealPart(xa[0]) + PetscRealPart(xb[0])), vals));
353         for (PetscInt i = 0; i < n; ++i) {
354           PetscCall(DMPlexPointLocalRead(dm, cone[1], sol[i], &svals));
355           for (PetscInt l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
356         }
357         PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(xb[0]), vals));
358       }
359     }
360   } else {
361     // P_1
362     for (PetscInt v = vStart; v < vEnd; ++v) {
363       PetscScalar *x, *svals;
364 
365       PetscCall(DMPlexPointLocalRead(cdm, v, coords, &x));
366       for (PetscInt i = 0; i < n; ++i) {
367         PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
368         for (PetscInt l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
369       }
370       PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
371     }
372   }
373   PetscCall(VecRestoreArrayRead(coordinates, &coords));
374   for (PetscInt i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
375   for (PetscInt l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
376   PetscCall(PetscFree3(sol, names, vals));
377 
378   PetscCall(PetscDrawLGDraw(lg));
379   PetscCall(PetscDrawLGDestroy(&lg));
380   PetscFunctionReturn(PETSC_SUCCESS);
381 }
382 
383 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
384 {
385   DM dm;
386 
387   PetscFunctionBegin;
388   PetscCall(VecGetDM(u, &dm));
389   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
390   PetscFunctionReturn(PETSC_SUCCESS);
391 }
392 
393 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
394 {
395   DM                 dm;
396   PetscSection       s;
397   PetscDraw          draw, popup;
398   DM                 cdm;
399   PetscSection       coordSection;
400   Vec                coordinates;
401   const PetscScalar *array;
402   PetscReal          lbound[3], ubound[3];
403   PetscReal          vbound[2], time;
404   PetscBool          flg;
405   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
406   const char        *name;
407   char               title[PETSC_MAX_PATH_LEN];
408 
409   PetscFunctionBegin;
410   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
411   PetscCall(VecGetDM(v, &dm));
412   PetscCall(DMGetCoordinateDim(dm, &dim));
413   PetscCall(DMGetLocalSection(dm, &s));
414   PetscCall(PetscSectionGetNumFields(s, &Nf));
415   PetscCall(DMGetCoarsenLevel(dm, &level));
416   PetscCall(DMGetCoordinateDM(dm, &cdm));
417   PetscCall(DMGetLocalSection(cdm, &coordSection));
418   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
419   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
420   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
421 
422   PetscCall(PetscObjectGetName((PetscObject)v, &name));
423   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
424 
425   PetscCall(VecGetLocalSize(coordinates, &N));
426   PetscCall(DMGetBoundingBox(dm, lbound, ubound));
427   PetscCall(PetscDrawClear(draw));
428 
429   /* Could implement something like DMDASelectFields() */
430   for (f = 0; f < Nf; ++f) {
431     DM          fdm = dm;
432     Vec         fv  = v;
433     IS          fis;
434     char        prefix[PETSC_MAX_PATH_LEN];
435     const char *fname;
436 
437     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
438     PetscCall(PetscSectionGetFieldName(s, f, &fname));
439 
440     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
441     else prefix[0] = '\0';
442     if (Nf > 1) {
443       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
444       PetscCall(VecGetSubVector(v, fis, &fv));
445       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
446       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
447     }
448     for (comp = 0; comp < Nc; ++comp, ++w) {
449       PetscInt nmax = 2;
450 
451       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
452       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
453       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
454       PetscCall(PetscDrawSetTitle(draw, title));
455 
456       /* TODO Get max and min only for this component */
457       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
458       if (!flg) {
459         PetscCall(VecMin(fv, NULL, &vbound[0]));
460         PetscCall(VecMax(fv, NULL, &vbound[1]));
461         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
462       }
463 
464       PetscCall(PetscDrawGetPopup(draw, &popup));
465       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
466       PetscCall(PetscDrawSetCoordinates(draw, lbound[0], lbound[1], ubound[0], ubound[1]));
467       PetscCall(VecGetArrayRead(fv, &array));
468       for (c = cStart; c < cEnd; ++c) {
469         DMPolytopeType     ct;
470         PetscScalar       *coords = NULL, *a = NULL;
471         const PetscScalar *coords_arr;
472         PetscBool          isDG;
473         PetscInt           numCoords;
474         int                color[4] = {-1, -1, -1, -1};
475 
476         PetscCall(DMPlexGetCellType(dm, c, &ct));
477         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
478         if (a) {
479           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
480           color[1] = color[2] = color[3] = color[0];
481         } else {
482           PetscScalar *vals = NULL;
483           PetscInt     numVals, va;
484 
485           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
486           if (!numVals) {
487             PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
488             continue;
489           }
490           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);
491           switch (numVals / Nc) {
492           case 1: /* P1 Clamped Segment Prism */
493           case 2: /* P1 Segment Prism, P2 Clamped Segment Prism */
494             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]);
495             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
496             break;
497           case 3: /* P1 Triangle */
498           case 4: /* P1 Quadrangle */
499             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]);
500             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
501             break;
502           case 6: /* P2 Triangle */
503           case 8: /* P2 Quadrangle */
504             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]);
505             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
506             break;
507           default:
508             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
509           }
510           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
511         }
512         PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
513         switch (numCoords) {
514         case 6:
515         case 12: /* Localized triangle */
516           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]));
517           break;
518         case 8:
519         case 16: /* Localized quadrilateral */
520           if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR) {
521             PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscMax(color[0], color[1])));
522           } else {
523             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]));
524             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]));
525           }
526           break;
527         default:
528           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
529         }
530         PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
531       }
532       PetscCall(VecRestoreArrayRead(fv, &array));
533       PetscCall(PetscDrawFlush(draw));
534       PetscCall(PetscDrawPause(draw));
535       PetscCall(PetscDrawSave(draw));
536     }
537     if (Nf > 1) {
538       PetscCall(VecRestoreSubVector(v, fis, &fv));
539       PetscCall(ISDestroy(&fis));
540       PetscCall(DMDestroy(&fdm));
541     }
542   }
543   PetscFunctionReturn(PETSC_SUCCESS);
544 }
545 
546 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
547 {
548   DM        dm;
549   PetscDraw draw;
550   PetscInt  dim;
551   PetscBool isnull;
552 
553   PetscFunctionBegin;
554   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
555   PetscCall(PetscDrawIsNull(draw, &isnull));
556   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
557 
558   PetscCall(VecGetDM(v, &dm));
559   PetscCall(DMGetCoordinateDim(dm, &dim));
560   switch (dim) {
561   case 1:
562     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
563     break;
564   case 2:
565     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
566     break;
567   default:
568     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
569   }
570   PetscFunctionReturn(PETSC_SUCCESS);
571 }
572 
573 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
574 {
575   DM                      dm;
576   Vec                     locv;
577   const char             *name;
578   PetscSection            section;
579   PetscInt                pStart, pEnd;
580   PetscInt                numFields;
581   PetscViewerVTKFieldType ft;
582 
583   PetscFunctionBegin;
584   PetscCall(VecGetDM(v, &dm));
585   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
586   PetscCall(PetscObjectGetName((PetscObject)v, &name));
587   PetscCall(PetscObjectSetName((PetscObject)locv, name));
588   PetscCall(VecCopy(v, locv));
589   PetscCall(DMGetLocalSection(dm, &section));
590   PetscCall(PetscSectionGetNumFields(section, &numFields));
591   if (!numFields) {
592     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
593     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
594   } else {
595     PetscInt f;
596 
597     for (f = 0; f < numFields; f++) {
598       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
599       if (ft == PETSC_VTK_INVALID) continue;
600       PetscCall(PetscObjectReference((PetscObject)locv));
601       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
602     }
603     PetscCall(VecDestroy(&locv));
604   }
605   PetscFunctionReturn(PETSC_SUCCESS);
606 }
607 
608 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
609 {
610   DM        dm;
611   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns, ispython;
612 
613   PetscFunctionBegin;
614   PetscCall(VecGetDM(v, &dm));
615   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
616   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
617   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
618   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
619   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
620   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
621   PetscCall(PetscObjectHasFunction((PetscObject)viewer, "PetscViewerPythonViewObject_C", &ispython));
622   if (isvtk || ishdf5 || isdraw || isglvis || iscgns || ispython) {
623     PetscInt    i, numFields;
624     PetscObject fe;
625     PetscBool   fem  = PETSC_FALSE;
626     Vec         locv = v;
627     const char *name;
628     PetscInt    step;
629     PetscReal   time;
630 
631     PetscCall(DMGetNumFields(dm, &numFields));
632     for (i = 0; i < numFields; i++) {
633       PetscCall(DMGetField(dm, i, NULL, &fe));
634       if (fe->classid == PETSCFE_CLASSID) {
635         fem = PETSC_TRUE;
636         break;
637       }
638     }
639     if (fem) {
640       PetscObject isZero;
641 
642       PetscCall(DMGetLocalVector(dm, &locv));
643       PetscCall(PetscObjectGetName((PetscObject)v, &name));
644       PetscCall(PetscObjectSetName((PetscObject)locv, name));
645       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
646       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
647       PetscCall(VecCopy(v, locv));
648       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
649       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
650     }
651     if (isvtk) {
652       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
653     } else if (ishdf5) {
654 #if defined(PETSC_HAVE_HDF5)
655       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
656 #else
657       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
658 #endif
659     } else if (isdraw) {
660       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
661     } else if (ispython) {
662       PetscCall(PetscViewerPythonViewObject(viewer, (PetscObject)locv));
663     } else if (isglvis) {
664       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
665       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
666       PetscCall(VecView_GLVis(locv, viewer));
667     } else if (iscgns) {
668 #if defined(PETSC_HAVE_CGNS)
669       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
670 #else
671       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
672 #endif
673     }
674     if (fem) {
675       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
676       PetscCall(DMRestoreLocalVector(dm, &locv));
677     }
678   } else {
679     PetscBool isseq;
680 
681     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
682     if (isseq) PetscCall(VecView_Seq(v, viewer));
683     else PetscCall(VecView_MPI(v, viewer));
684   }
685   PetscFunctionReturn(PETSC_SUCCESS);
686 }
687 
688 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
689 {
690   DM        dm;
691   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns, ispython;
692 
693   PetscFunctionBegin;
694   PetscCall(VecGetDM(v, &dm));
695   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
696   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
697   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
698   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
699   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
700   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
701   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
702   PetscCall(PetscObjectHasFunction((PetscObject)viewer, "PetscViewerPythonViewObject_C", &ispython));
703   if (isvtk || isdraw || isglvis || iscgns || ispython) {
704     Vec         locv;
705     PetscObject isZero;
706     const char *name;
707 
708     PetscCall(DMGetLocalVector(dm, &locv));
709     PetscCall(PetscObjectGetName((PetscObject)v, &name));
710     PetscCall(PetscObjectSetName((PetscObject)locv, name));
711     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
712     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
713     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
714     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
715     PetscCall(VecView_Plex_Local(locv, viewer));
716     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
717     PetscCall(DMRestoreLocalVector(dm, &locv));
718   } else if (ishdf5) {
719 #if defined(PETSC_HAVE_HDF5)
720     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
721 #else
722     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
723 #endif
724   } else if (isexodusii) {
725 #if defined(PETSC_HAVE_EXODUSII)
726     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
727 #else
728     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
729 #endif
730   } else {
731     PetscBool isseq;
732 
733     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
734     if (isseq) PetscCall(VecView_Seq(v, viewer));
735     else PetscCall(VecView_MPI(v, viewer));
736   }
737   PetscFunctionReturn(PETSC_SUCCESS);
738 }
739 
740 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
741 {
742   DM                dm;
743   MPI_Comm          comm;
744   PetscViewerFormat format;
745   Vec               v;
746   PetscBool         isvtk, ishdf5;
747 
748   PetscFunctionBegin;
749   PetscCall(VecGetDM(originalv, &dm));
750   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
751   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
752   PetscCall(PetscViewerGetFormat(viewer, &format));
753   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
754   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
755   if (format == PETSC_VIEWER_NATIVE) {
756     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
757     /* this need a better fix */
758     if (dm->useNatural) {
759       if (dm->sfNatural) {
760         const char *vecname;
761         PetscInt    n, nroots;
762 
763         PetscCall(VecGetLocalSize(originalv, &n));
764         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
765         if (n == nroots) {
766           PetscCall(DMPlexCreateNaturalVector(dm, &v));
767           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
768           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
769           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
770           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
771         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
772       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
773     } else v = originalv;
774   } else v = originalv;
775 
776   if (ishdf5) {
777 #if defined(PETSC_HAVE_HDF5)
778     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
779 #else
780     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
781 #endif
782   } else if (isvtk) {
783     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
784   } else {
785     PetscBool isseq;
786 
787     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
788     if (isseq) PetscCall(VecView_Seq(v, viewer));
789     else PetscCall(VecView_MPI(v, viewer));
790   }
791   if (v != originalv) PetscCall(VecDestroy(&v));
792   PetscFunctionReturn(PETSC_SUCCESS);
793 }
794 
795 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
796 {
797   DM        dm;
798   PetscBool ishdf5;
799 
800   PetscFunctionBegin;
801   PetscCall(VecGetDM(v, &dm));
802   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
803   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
804   if (ishdf5) {
805     DM          dmBC;
806     Vec         gv;
807     const char *name;
808 
809     PetscCall(DMGetOutputDM(dm, &dmBC));
810     PetscCall(DMGetGlobalVector(dmBC, &gv));
811     PetscCall(PetscObjectGetName((PetscObject)v, &name));
812     PetscCall(PetscObjectSetName((PetscObject)gv, name));
813     PetscCall(VecLoad_Default(gv, viewer));
814     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
815     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
816     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
817   } else PetscCall(VecLoad_Default(v, viewer));
818   PetscFunctionReturn(PETSC_SUCCESS);
819 }
820 
821 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
822 {
823   DM        dm;
824   PetscBool ishdf5, isexodusii, iscgns;
825 
826   PetscFunctionBegin;
827   PetscCall(VecGetDM(v, &dm));
828   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
829   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
830   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
831   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
832   if (ishdf5) {
833 #if defined(PETSC_HAVE_HDF5)
834     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
835 #else
836     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
837 #endif
838   } else if (isexodusii) {
839 #if defined(PETSC_HAVE_EXODUSII)
840     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
841 #else
842     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
843 #endif
844   } else if (iscgns) {
845 #if defined(PETSC_HAVE_CGNS)
846     PetscCall(VecLoad_Plex_CGNS_Internal(v, viewer));
847 #else
848     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
849 #endif
850   } else PetscCall(VecLoad_Default(v, viewer));
851   PetscFunctionReturn(PETSC_SUCCESS);
852 }
853 
854 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
855 {
856   DM                dm;
857   PetscViewerFormat format;
858   PetscBool         ishdf5;
859 
860   PetscFunctionBegin;
861   PetscCall(VecGetDM(originalv, &dm));
862   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
863   PetscCall(PetscViewerGetFormat(viewer, &format));
864   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
865   if (format == PETSC_VIEWER_NATIVE) {
866     if (dm->useNatural) {
867       if (dm->sfNatural) {
868         if (ishdf5) {
869 #if defined(PETSC_HAVE_HDF5)
870           Vec         v;
871           const char *vecname;
872 
873           PetscCall(DMPlexCreateNaturalVector(dm, &v));
874           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
875           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
876           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
877           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
878           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
879           PetscCall(VecDestroy(&v));
880 #else
881           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
882 #endif
883         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
884       }
885     } else PetscCall(VecLoad_Default(originalv, viewer));
886   }
887   PetscFunctionReturn(PETSC_SUCCESS);
888 }
889 
890 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
891 {
892   PetscSection       coordSection;
893   Vec                coordinates;
894   DMLabel            depthLabel, celltypeLabel;
895   const char        *name[4];
896   const PetscScalar *a;
897   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
898 
899   PetscFunctionBegin;
900   PetscCall(DMGetDimension(dm, &dim));
901   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
902   PetscCall(DMGetCoordinateSection(dm, &coordSection));
903   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
904   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
905   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
906   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
907   PetscCall(VecGetArrayRead(coordinates, &a));
908   name[0]       = "vertex";
909   name[1]       = "edge";
910   name[dim - 1] = "face";
911   name[dim]     = "cell";
912   for (c = cStart; c < cEnd; ++c) {
913     PetscInt *closure = NULL;
914     PetscInt  closureSize, cl, ct;
915 
916     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
917     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
918     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
919     PetscCall(PetscViewerASCIIPushTab(viewer));
920     for (cl = 0; cl < closureSize * 2; cl += 2) {
921       PetscInt point = closure[cl], depth, dof, off, d, p;
922 
923       if ((point < pStart) || (point >= pEnd)) continue;
924       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
925       if (!dof) continue;
926       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
927       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
928       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
929       for (p = 0; p < dof / dim; ++p) {
930         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
931         for (d = 0; d < dim; ++d) {
932           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
933           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
934         }
935         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
936       }
937       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
938     }
939     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
940     PetscCall(PetscViewerASCIIPopTab(viewer));
941   }
942   PetscCall(VecRestoreArrayRead(coordinates, &a));
943   PetscFunctionReturn(PETSC_SUCCESS);
944 }
945 
946 typedef enum {
947   CS_CARTESIAN,
948   CS_POLAR,
949   CS_CYLINDRICAL,
950   CS_SPHERICAL
951 } CoordSystem;
952 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
953 
954 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
955 {
956   PetscInt i;
957 
958   PetscFunctionBegin;
959   if (dim > 3) {
960     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
961   } else {
962     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
963 
964     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
965     switch (cs) {
966     case CS_CARTESIAN:
967       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
968       break;
969     case CS_POLAR:
970       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
971       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
972       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
973       break;
974     case CS_CYLINDRICAL:
975       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
976       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
977       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
978       trcoords[2] = coords[2];
979       break;
980     case CS_SPHERICAL:
981       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
982       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
983       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
984       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
985       break;
986     }
987     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
988   }
989   PetscFunctionReturn(PETSC_SUCCESS);
990 }
991 
992 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
993 {
994   DM_Plex          *mesh = (DM_Plex *)dm->data;
995   DM                cdm, cdmCell;
996   PetscSection      coordSection, coordSectionCell;
997   Vec               coordinates, coordinatesCell;
998   PetscViewerFormat format;
999 
1000   PetscFunctionBegin;
1001   PetscCall(PetscViewerGetFormat(viewer, &format));
1002   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
1003     const char *name;
1004     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
1005     PetscInt    pStart, pEnd, p, numLabels, l;
1006     PetscMPIInt rank, size;
1007 
1008     PetscCall(DMGetCoordinateDM(dm, &cdm));
1009     PetscCall(DMGetCoordinateSection(dm, &coordSection));
1010     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1011     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
1012     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
1013     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
1014     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1015     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1016     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1017     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1018     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
1019     PetscCall(DMGetDimension(dm, &dim));
1020     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1021     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1022     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1023     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1024     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
1025     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1026     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
1027     for (p = pStart; p < pEnd; ++p) {
1028       PetscInt dof, off, s;
1029 
1030       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
1031       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
1032       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
1033     }
1034     PetscCall(PetscViewerFlush(viewer));
1035     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
1036     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
1037     for (p = pStart; p < pEnd; ++p) {
1038       PetscInt dof, off, c;
1039 
1040       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
1041       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
1042       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]));
1043     }
1044     PetscCall(PetscViewerFlush(viewer));
1045     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1046     if (coordSection && coordinates) {
1047       CoordSystem        cs = CS_CARTESIAN;
1048       const PetscScalar *array, *arrayCell = NULL;
1049       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_INT_MAX, pcEnd = PETSC_INT_MIN, pStart, pEnd, p;
1050       PetscMPIInt        rank;
1051       const char        *name;
1052 
1053       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
1054       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
1055       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
1056       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
1057       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
1058       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
1059       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
1060       pStart = PetscMin(pvStart, pcStart);
1061       pEnd   = PetscMax(pvEnd, pcEnd);
1062       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
1063       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
1064       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
1065       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
1066 
1067       PetscCall(VecGetArrayRead(coordinates, &array));
1068       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
1069       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1070       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
1071       for (p = pStart; p < pEnd; ++p) {
1072         PetscInt dof, off;
1073 
1074         if (p >= pvStart && p < pvEnd) {
1075           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
1076           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
1077           if (dof) {
1078             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dof %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1079             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
1080             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1081           }
1082         }
1083         if (cdmCell && p >= pcStart && p < pcEnd) {
1084           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
1085           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
1086           if (dof) {
1087             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dof %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1088             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
1089             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1090           }
1091         }
1092       }
1093       PetscCall(PetscViewerFlush(viewer));
1094       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1095       PetscCall(VecRestoreArrayRead(coordinates, &array));
1096       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
1097     }
1098     PetscCall(DMGetNumLabels(dm, &numLabels));
1099     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1100     for (l = 0; l < numLabels; ++l) {
1101       DMLabel     label;
1102       PetscBool   isdepth;
1103       const char *name;
1104 
1105       PetscCall(DMGetLabelName(dm, l, &name));
1106       PetscCall(PetscStrcmp(name, "depth", &isdepth));
1107       if (isdepth) continue;
1108       PetscCall(DMGetLabel(dm, name, &label));
1109       PetscCall(DMLabelView(label, viewer));
1110     }
1111     if (size > 1) {
1112       PetscSF sf;
1113 
1114       PetscCall(DMGetPointSF(dm, &sf));
1115       PetscCall(PetscSFView(sf, viewer));
1116     }
1117     if (mesh->periodic.face_sfs)
1118       for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFView(mesh->periodic.face_sfs[i], viewer));
1119     PetscCall(PetscViewerFlush(viewer));
1120   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
1121     const char  *name, *color;
1122     const char  *defcolors[3]  = {"gray", "orange", "green"};
1123     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
1124     char         lname[PETSC_MAX_PATH_LEN];
1125     PetscReal    scale      = 2.0;
1126     PetscReal    tikzscale  = 1.0;
1127     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
1128     double       tcoords[3];
1129     PetscScalar *coords;
1130     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;
1131     PetscMPIInt  rank, size;
1132     char       **names, **colors, **lcolors;
1133     PetscBool    flg, lflg;
1134     PetscBT      wp = NULL;
1135     PetscInt     pEnd, pStart;
1136 
1137     PetscCall(DMGetCoordinateDM(dm, &cdm));
1138     PetscCall(DMGetCoordinateSection(dm, &coordSection));
1139     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1140     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
1141     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
1142     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
1143     PetscCall(DMGetDimension(dm, &dim));
1144     PetscCall(DMPlexGetDepth(dm, &depth));
1145     PetscCall(DMGetNumLabels(dm, &numLabels));
1146     numLabels  = PetscMax(numLabels, 10);
1147     numColors  = 10;
1148     numLColors = 10;
1149     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
1150     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
1151     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
1152     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
1153     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
1154     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
1155     n = 4;
1156     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
1157     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1158     n = 4;
1159     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
1160     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1161     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
1162     if (!useLabels) numLabels = 0;
1163     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
1164     if (!useColors) {
1165       numColors = 3;
1166       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
1167     }
1168     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
1169     if (!useColors) {
1170       numLColors = 4;
1171       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
1172     }
1173     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
1174     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
1175     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
1176     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
1177     if (depth < dim) plotEdges = PETSC_FALSE;
1178     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
1179 
1180     /* filter points with labelvalue != labeldefaultvalue */
1181     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1182     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1183     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1184     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1185     PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
1186     if (lflg) {
1187       DMLabel lbl;
1188 
1189       PetscCall(DMGetLabel(dm, lname, &lbl));
1190       if (lbl) {
1191         PetscInt val, defval;
1192 
1193         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1194         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1195         for (c = pStart; c < pEnd; c++) {
1196           PetscInt *closure = NULL;
1197           PetscInt  closureSize;
1198 
1199           PetscCall(DMLabelGetValue(lbl, c, &val));
1200           if (val == defval) continue;
1201 
1202           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1203           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1204           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1205         }
1206       }
1207     }
1208 
1209     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1210     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1211     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1212     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1213 \\documentclass[tikz]{standalone}\n\n\
1214 \\usepackage{pgflibraryshapes}\n\
1215 \\usetikzlibrary{backgrounds}\n\
1216 \\usetikzlibrary{arrows}\n\
1217 \\begin{document}\n"));
1218     if (size > 1) {
1219       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1220       for (p = 0; p < size; ++p) {
1221         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1222         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1223       }
1224       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1225     }
1226     if (drawHasse) {
1227       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, PetscMax(fEnd - fStart, cEnd - cStart)));
1228 
1229       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1230       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1231       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1232       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1233       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1234       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1235       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1236       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1237       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fStart}{%" PetscInt_FMT "}\n", fStart));
1238       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fEnd}{%" PetscInt_FMT "}\n", fEnd - 1));
1239       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fShift}{%.2f}\n", 3 + (maxStratum - (fEnd - fStart)) / 2.));
1240       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numFaces}{%" PetscInt_FMT "}\n", fEnd - fStart));
1241       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1242       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1243       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1244       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1245     }
1246     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1247 
1248     /* Plot vertices */
1249     PetscCall(VecGetArray(coordinates, &coords));
1250     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1251     for (v = vStart; v < vEnd; ++v) {
1252       PetscInt  off, dof, d;
1253       PetscBool isLabeled = PETSC_FALSE;
1254 
1255       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1256       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1257       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1258       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1259       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1260       for (d = 0; d < dof; ++d) {
1261         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1262         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1263       }
1264       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1265       if (dim == 3) {
1266         PetscReal tmp = tcoords[1];
1267         tcoords[1]    = tcoords[2];
1268         tcoords[2]    = -tmp;
1269       }
1270       for (d = 0; d < dof; ++d) {
1271         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1272         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", tcoords[d]));
1273       }
1274       if (drawHasse) color = colors[0 % numColors];
1275       else color = colors[rank % numColors];
1276       for (l = 0; l < numLabels; ++l) {
1277         PetscInt val;
1278         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1279         if (val >= 0) {
1280           color     = lcolors[l % numLColors];
1281           isLabeled = PETSC_TRUE;
1282           break;
1283         }
1284       }
1285       if (drawNumbers[0]) {
1286         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1287       } else if (drawColors[0]) {
1288         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1289       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1290     }
1291     PetscCall(VecRestoreArray(coordinates, &coords));
1292     PetscCall(PetscViewerFlush(viewer));
1293     /* Plot edges */
1294     if (plotEdges) {
1295       PetscCall(VecGetArray(coordinates, &coords));
1296       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1297       for (e = eStart; e < eEnd; ++e) {
1298         const PetscInt *cone;
1299         PetscInt        coneSize, offA, offB, dof, d;
1300 
1301         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1302         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1303         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1304         PetscCall(DMPlexGetCone(dm, e, &cone));
1305         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1306         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1307         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1308         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1309         for (d = 0; d < dof; ++d) {
1310           tcoords[d] = (double)(scale * PetscRealPart(coords[offA + d] + coords[offB + d]) / 2);
1311           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1312         }
1313         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1314         if (dim == 3) {
1315           PetscReal tmp = tcoords[1];
1316           tcoords[1]    = tcoords[2];
1317           tcoords[2]    = -tmp;
1318         }
1319         for (d = 0; d < dof; ++d) {
1320           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1321           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", tcoords[d]));
1322         }
1323         if (drawHasse) color = colors[1 % numColors];
1324         else color = colors[rank % numColors];
1325         for (l = 0; l < numLabels; ++l) {
1326           PetscInt val;
1327           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1328           if (val >= 0) {
1329             color = lcolors[l % numLColors];
1330             break;
1331           }
1332         }
1333         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1334       }
1335       PetscCall(VecRestoreArray(coordinates, &coords));
1336       PetscCall(PetscViewerFlush(viewer));
1337       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1338     }
1339     /* Plot cells */
1340     if (dim == 3 || !drawNumbers[1]) {
1341       for (e = eStart; e < eEnd; ++e) {
1342         const PetscInt *cone;
1343 
1344         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1345         color = colors[rank % numColors];
1346         for (l = 0; l < numLabels; ++l) {
1347           PetscInt val;
1348           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1349           if (val >= 0) {
1350             color = lcolors[l % numLColors];
1351             break;
1352           }
1353         }
1354         PetscCall(DMPlexGetCone(dm, e, &cone));
1355         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1356       }
1357     } else {
1358       DMPolytopeType ct;
1359 
1360       /* Drawing a 2D polygon */
1361       for (c = cStart; c < cEnd; ++c) {
1362         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1363         PetscCall(DMPlexGetCellType(dm, c, &ct));
1364         if (DMPolytopeTypeIsHybrid(ct)) {
1365           const PetscInt *cone;
1366           PetscInt        coneSize, e;
1367 
1368           PetscCall(DMPlexGetCone(dm, c, &cone));
1369           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1370           for (e = 0; e < coneSize; ++e) {
1371             const PetscInt *econe;
1372 
1373             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1374             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));
1375           }
1376         } else {
1377           PetscInt *closure = NULL;
1378           PetscInt  closureSize, Nv = 0, v;
1379 
1380           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1381           for (p = 0; p < closureSize * 2; p += 2) {
1382             const PetscInt point = closure[p];
1383 
1384             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1385           }
1386           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1387           for (v = 0; v <= Nv; ++v) {
1388             const PetscInt vertex = closure[v % Nv];
1389 
1390             if (v > 0) {
1391               if (plotEdges) {
1392                 const PetscInt *edge;
1393                 PetscInt        endpoints[2], ne;
1394 
1395                 endpoints[0] = closure[v - 1];
1396                 endpoints[1] = vertex;
1397                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1398                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1399                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1400                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1401               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1402             }
1403             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1404           }
1405           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1406           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1407         }
1408       }
1409     }
1410     for (c = cStart; c < cEnd; ++c) {
1411       double             ccoords[3] = {0.0, 0.0, 0.0};
1412       PetscBool          isLabeled  = PETSC_FALSE;
1413       PetscScalar       *cellCoords = NULL;
1414       const PetscScalar *array;
1415       PetscInt           numCoords, cdim, d;
1416       PetscBool          isDG;
1417 
1418       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1419       PetscCall(DMGetCoordinateDim(dm, &cdim));
1420       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1421       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1422       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1423       for (p = 0; p < numCoords / cdim; ++p) {
1424         for (d = 0; d < cdim; ++d) {
1425           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1426           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1427         }
1428         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1429         if (cdim == 3) {
1430           PetscReal tmp = tcoords[1];
1431           tcoords[1]    = tcoords[2];
1432           tcoords[2]    = -tmp;
1433         }
1434         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1435       }
1436       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1437       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1438       for (d = 0; d < cdim; ++d) {
1439         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1440         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", ccoords[d]));
1441       }
1442       if (drawHasse) color = colors[depth % numColors];
1443       else color = colors[rank % numColors];
1444       for (l = 0; l < numLabels; ++l) {
1445         PetscInt val;
1446         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1447         if (val >= 0) {
1448           color     = lcolors[l % numLColors];
1449           isLabeled = PETSC_TRUE;
1450           break;
1451         }
1452       }
1453       if (drawNumbers[dim]) {
1454         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1455       } else if (drawColors[dim]) {
1456         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1457       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1458     }
1459     if (drawHasse) {
1460       int height = 0;
1461 
1462       color = colors[depth % numColors];
1463       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1464       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1465       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1466       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,%d) {\\c};\n", rank, color, height++));
1467       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1468 
1469       if (depth > 2) {
1470         color = colors[1 % numColors];
1471         PetscCall(PetscViewerASCIIPrintf(viewer, "%% Faces\n"));
1472         PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\f in {\\fStart,...,\\fEnd}\n"));
1473         PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1474         PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\f_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\fShift+\\f-\\fStart,%d) {\\f};\n", rank, color, height++));
1475         PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1476       }
1477 
1478       color = colors[1 % numColors];
1479       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1480       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1481       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1482       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,%d) {\\e};\n", rank, color, height++));
1483       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1484 
1485       color = colors[0 % numColors];
1486       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1487       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1488       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1489       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,%d) {\\v};\n", rank, color, height++));
1490       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1491 
1492       for (p = pStart; p < pEnd; ++p) {
1493         const PetscInt *cone;
1494         PetscInt        coneSize, cp;
1495 
1496         PetscCall(DMPlexGetCone(dm, p, &cone));
1497         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1498         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1499       }
1500     }
1501     PetscCall(PetscViewerFlush(viewer));
1502     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1503     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1504     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1505     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1506     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1507     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1508     PetscCall(PetscFree3(names, colors, lcolors));
1509     PetscCall(PetscBTDestroy(&wp));
1510   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1511     Vec                    cown, acown;
1512     VecScatter             sct;
1513     ISLocalToGlobalMapping g2l;
1514     IS                     gid, acis;
1515     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1516     MPI_Group              ggroup, ngroup;
1517     PetscScalar           *array, nid;
1518     const PetscInt        *idxs;
1519     PetscInt              *idxs2, *start, *adjacency, *work;
1520     PetscInt64             lm[3], gm[3];
1521     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1522     PetscMPIInt            d1, d2, rank;
1523 
1524     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1525     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1526 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1527     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1528 #endif
1529     if (ncomm != MPI_COMM_NULL) {
1530       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1531       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1532       d1 = 0;
1533       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1534       nid = d2;
1535       PetscCallMPI(MPI_Group_free(&ggroup));
1536       PetscCallMPI(MPI_Group_free(&ngroup));
1537       PetscCallMPI(MPI_Comm_free(&ncomm));
1538     } else nid = 0.0;
1539 
1540     /* Get connectivity */
1541     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1542     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1543 
1544     /* filter overlapped local cells */
1545     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1546     PetscCall(ISGetIndices(gid, &idxs));
1547     PetscCall(ISGetLocalSize(gid, &cum));
1548     PetscCall(PetscMalloc1(cum, &idxs2));
1549     for (c = cStart, cum = 0; c < cEnd; c++) {
1550       if (idxs[c - cStart] < 0) continue;
1551       idxs2[cum++] = idxs[c - cStart];
1552     }
1553     PetscCall(ISRestoreIndices(gid, &idxs));
1554     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1555     PetscCall(ISDestroy(&gid));
1556     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1557 
1558     /* support for node-aware cell locality */
1559     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1560     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1561     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1562     PetscCall(VecGetArray(cown, &array));
1563     for (c = 0; c < numVertices; c++) array[c] = nid;
1564     PetscCall(VecRestoreArray(cown, &array));
1565     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1566     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1567     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1568     PetscCall(ISDestroy(&acis));
1569     PetscCall(VecScatterDestroy(&sct));
1570     PetscCall(VecDestroy(&cown));
1571 
1572     /* compute edgeCut */
1573     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1574     PetscCall(PetscMalloc1(cum, &work));
1575     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1576     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1577     PetscCall(ISDestroy(&gid));
1578     PetscCall(VecGetArray(acown, &array));
1579     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1580       PetscInt totl;
1581 
1582       totl = start[c + 1] - start[c];
1583       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1584       for (i = 0; i < totl; i++) {
1585         if (work[i] < 0) {
1586           ect += 1;
1587           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1588         }
1589       }
1590     }
1591     PetscCall(PetscFree(work));
1592     PetscCall(VecRestoreArray(acown, &array));
1593     lm[0] = numVertices > 0 ? numVertices : PETSC_INT_MAX;
1594     lm[1] = -numVertices;
1595     PetscCallMPI(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1596     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt64_FMT ", min %" PetscInt64_FMT, -((double)gm[1]) / ((double)gm[0]), -gm[1], gm[0]));
1597     lm[0] = ect;                     /* edgeCut */
1598     lm[1] = ectn;                    /* node-aware edgeCut */
1599     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1600     PetscCallMPI(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1601     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt64_FMT ")\n", gm[2]));
1602 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1603     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt64_FMT " (on node %.3f)\n", gm[0] / 2, gm[0] ? ((double)gm[1]) / ((double)gm[0]) : 1.));
1604 #else
1605     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt64_FMT " (on node %.3f)\n", gm[0] / 2, 0.0));
1606 #endif
1607     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1608     PetscCall(PetscFree(start));
1609     PetscCall(PetscFree(adjacency));
1610     PetscCall(VecDestroy(&acown));
1611   } else {
1612     const char    *name;
1613     PetscInt      *sizes, *hybsizes, *ghostsizes;
1614     PetscInt       locDepth, depth, cellHeight, dim, d;
1615     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1616     PetscInt       numLabels, l, maxSize = 17;
1617     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1618     MPI_Comm       comm;
1619     PetscMPIInt    size, rank;
1620 
1621     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1622     PetscCallMPI(MPI_Comm_size(comm, &size));
1623     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1624     PetscCall(DMGetDimension(dm, &dim));
1625     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1626     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1627     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1628     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1629     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1630     PetscCall(DMPlexGetDepth(dm, &locDepth));
1631     PetscCallMPI(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1632     PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &gcStart, &gcEnd));
1633     gcNum = gcEnd - gcStart;
1634     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1635     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1636     for (d = 0; d <= depth; d++) {
1637       PetscInt Nc[2] = {0, 0}, ict;
1638 
1639       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1640       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1641       ict = ct0;
1642       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1643       ct0 = (DMPolytopeType)ict;
1644       for (p = pStart; p < pEnd; ++p) {
1645         DMPolytopeType ct;
1646 
1647         PetscCall(DMPlexGetCellType(dm, p, &ct));
1648         if (ct == ct0) ++Nc[0];
1649         else ++Nc[1];
1650       }
1651       if (size < maxSize) {
1652         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1653         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1654         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1655         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1656         for (p = 0; p < size; ++p) {
1657           if (rank == 0) {
1658             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1659             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1660             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1661           }
1662         }
1663       } else {
1664         PetscInt locMinMax[2];
1665 
1666         locMinMax[0] = Nc[0] + Nc[1];
1667         locMinMax[1] = Nc[0] + Nc[1];
1668         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1669         locMinMax[0] = Nc[1];
1670         locMinMax[1] = Nc[1];
1671         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1672         if (d == depth) {
1673           locMinMax[0] = gcNum;
1674           locMinMax[1] = gcNum;
1675           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1676         }
1677         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1678         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1679         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1680         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1681       }
1682       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1683     }
1684     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1685     {
1686       const PetscReal *maxCell;
1687       const PetscReal *L;
1688       PetscBool        localized;
1689 
1690       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1691       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1692       if (L || localized) {
1693         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1694         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1695         if (L) {
1696           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1697           for (d = 0; d < dim; ++d) {
1698             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1699             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1700           }
1701           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1702         }
1703         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1704         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1705       }
1706     }
1707     PetscCall(DMGetNumLabels(dm, &numLabels));
1708     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1709     for (l = 0; l < numLabels; ++l) {
1710       DMLabel     label;
1711       const char *name;
1712       PetscInt   *values;
1713       PetscInt    numValues, v;
1714 
1715       PetscCall(DMGetLabelName(dm, l, &name));
1716       PetscCall(DMGetLabel(dm, name, &label));
1717       PetscCall(DMLabelGetNumValues(label, &numValues));
1718       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1719 
1720       { // Extract array of DMLabel values so it can be sorted
1721         IS              is_values;
1722         const PetscInt *is_values_local = NULL;
1723 
1724         PetscCall(DMLabelGetValueIS(label, &is_values));
1725         PetscCall(ISGetIndices(is_values, &is_values_local));
1726         PetscCall(PetscMalloc1(numValues, &values));
1727         PetscCall(PetscArraycpy(values, is_values_local, numValues));
1728         PetscCall(PetscSortInt(numValues, values));
1729         PetscCall(ISRestoreIndices(is_values, &is_values_local));
1730         PetscCall(ISDestroy(&is_values));
1731       }
1732       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1733       for (v = 0; v < numValues; ++v) {
1734         PetscInt size;
1735 
1736         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1737         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1738         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1739       }
1740       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1741       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1742       PetscCall(PetscFree(values));
1743     }
1744     {
1745       char    **labelNames;
1746       PetscInt  Nl = numLabels;
1747       PetscBool flg;
1748 
1749       PetscCall(PetscMalloc1(Nl, &labelNames));
1750       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1751       for (l = 0; l < Nl; ++l) {
1752         DMLabel label;
1753 
1754         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1755         if (flg) {
1756           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1757           PetscCall(DMLabelView(label, viewer));
1758         }
1759         PetscCall(PetscFree(labelNames[l]));
1760       }
1761       PetscCall(PetscFree(labelNames));
1762     }
1763     /* If no fields are specified, people do not want to see adjacency */
1764     if (dm->Nf) {
1765       PetscInt f;
1766 
1767       for (f = 0; f < dm->Nf; ++f) {
1768         const char *name;
1769 
1770         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1771         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1772         PetscCall(PetscViewerASCIIPushTab(viewer));
1773         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1774         if (dm->fields[f].adjacency[0]) {
1775           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1776           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1777         } else {
1778           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1779           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1780         }
1781         PetscCall(PetscViewerASCIIPopTab(viewer));
1782       }
1783     }
1784     PetscCall(DMGetCoarseDM(dm, &cdm));
1785     if (cdm) {
1786       PetscCall(PetscViewerASCIIPushTab(viewer));
1787       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1788       PetscCall(DMPlexView_Ascii(cdm, viewer));
1789       PetscCall(PetscViewerASCIIPopTab(viewer));
1790     }
1791   }
1792   PetscFunctionReturn(PETSC_SUCCESS);
1793 }
1794 
1795 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1796 {
1797   DMPolytopeType ct;
1798   PetscMPIInt    rank;
1799   PetscInt       cdim;
1800 
1801   PetscFunctionBegin;
1802   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1803   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1804   PetscCall(DMGetCoordinateDim(dm, &cdim));
1805   switch (ct) {
1806   case DM_POLYTOPE_SEGMENT:
1807   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1808     switch (cdim) {
1809     case 1: {
1810       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1811       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1812 
1813       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1814       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1815       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1816     } break;
1817     case 2: {
1818       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1819       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1820       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1821 
1822       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1823       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));
1824       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));
1825     } break;
1826     default:
1827       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1828     }
1829     break;
1830   case DM_POLYTOPE_TRIANGLE:
1831     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));
1832     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1833     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1834     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1835     break;
1836   case DM_POLYTOPE_QUADRILATERAL:
1837     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));
1838     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));
1839     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1840     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1841     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1842     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1843     break;
1844   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1845     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));
1846     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));
1847     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1848     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1849     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1850     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1851     break;
1852   case DM_POLYTOPE_FV_GHOST:
1853     break;
1854   default:
1855     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1856   }
1857   PetscFunctionReturn(PETSC_SUCCESS);
1858 }
1859 
1860 static PetscErrorCode DrawPolygon_Private(DM dm, PetscDraw draw, PetscInt cell, PetscInt Nv, const PetscReal refVertices[], const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1861 {
1862   PetscReal   centroid[2] = {0., 0.};
1863   PetscMPIInt rank;
1864   PetscMPIInt fillColor;
1865 
1866   PetscFunctionBegin;
1867   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1868   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1869   for (PetscInt v = 0; v < Nv; ++v) {
1870     centroid[0] += PetscRealPart(coords[v * 2 + 0]) / Nv;
1871     centroid[1] += PetscRealPart(coords[v * 2 + 1]) / Nv;
1872   }
1873   for (PetscInt e = 0; e < Nv; ++e) {
1874     refCoords[0] = refVertices[e * 2 + 0];
1875     refCoords[1] = refVertices[e * 2 + 1];
1876     for (PetscInt d = 1; d <= edgeDiv; ++d) {
1877       refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % Nv * 2 + 0] - refCoords[0]) * d / edgeDiv;
1878       refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % Nv * 2 + 1] - refCoords[1]) * d / edgeDiv;
1879     }
1880     PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1881     for (PetscInt d = 0; d < edgeDiv; ++d) {
1882       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));
1883       PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1884     }
1885   }
1886   PetscFunctionReturn(PETSC_SUCCESS);
1887 }
1888 
1889 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1890 {
1891   DMPolytopeType ct;
1892 
1893   PetscFunctionBegin;
1894   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1895   switch (ct) {
1896   case DM_POLYTOPE_TRIANGLE: {
1897     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1898 
1899     PetscCall(DrawPolygon_Private(dm, draw, cell, 3, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1900   } break;
1901   case DM_POLYTOPE_QUADRILATERAL: {
1902     PetscReal refVertices[8] = {-1., -1., 1., -1., 1., 1., -1., 1.};
1903 
1904     PetscCall(DrawPolygon_Private(dm, draw, cell, 4, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1905   } break;
1906   default:
1907     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1908   }
1909   PetscFunctionReturn(PETSC_SUCCESS);
1910 }
1911 
1912 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1913 {
1914   PetscDraw    draw;
1915   DM           cdm;
1916   PetscSection coordSection;
1917   Vec          coordinates;
1918   PetscReal    xyl[3], xyr[3];
1919   PetscReal   *refCoords, *edgeCoords;
1920   PetscBool    isnull, drawAffine;
1921   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, cDegree, edgeDiv;
1922 
1923   PetscFunctionBegin;
1924   PetscCall(DMGetCoordinateDim(dm, &dim));
1925   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1926   PetscCall(DMGetCoordinateDegree_Internal(dm, &cDegree));
1927   drawAffine = cDegree > 1 ? PETSC_FALSE : PETSC_TRUE;
1928   edgeDiv    = cDegree + 1;
1929   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1930   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1931   PetscCall(DMGetCoordinateDM(dm, &cdm));
1932   PetscCall(DMGetLocalSection(cdm, &coordSection));
1933   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1934   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1935   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1936 
1937   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1938   PetscCall(PetscDrawIsNull(draw, &isnull));
1939   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1940   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1941 
1942   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1943   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1944   PetscCall(PetscDrawClear(draw));
1945 
1946   for (c = cStart; c < cEnd; ++c) {
1947     PetscScalar       *coords = NULL;
1948     const PetscScalar *coords_arr;
1949     PetscInt           numCoords;
1950     PetscBool          isDG;
1951 
1952     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1953     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1954     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1955     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1956   }
1957   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1958   PetscCall(PetscDrawFlush(draw));
1959   PetscCall(PetscDrawPause(draw));
1960   PetscCall(PetscDrawSave(draw));
1961   PetscFunctionReturn(PETSC_SUCCESS);
1962 }
1963 
1964 static PetscErrorCode DMPlexCreateHighOrderSurrogate_Internal(DM dm, DM *hdm)
1965 {
1966   DM           odm = dm, rdm = dm, cdm;
1967   PetscFE      fe;
1968   PetscSpace   sp;
1969   PetscClassId id;
1970   PetscInt     degree;
1971   PetscBool    hoView = PETSC_TRUE;
1972 
1973   PetscFunctionBegin;
1974   PetscObjectOptionsBegin((PetscObject)dm);
1975   PetscCall(PetscOptionsBool("-dm_plex_high_order_view", "Subsample to view meshes with high order coordinates", "DMPlexCreateHighOrderSurrogate_Internal", hoView, &hoView, NULL));
1976   PetscOptionsEnd();
1977   PetscCall(PetscObjectReference((PetscObject)dm));
1978   *hdm = dm;
1979   if (!hoView) PetscFunctionReturn(PETSC_SUCCESS);
1980   PetscCall(DMGetCoordinateDM(dm, &cdm));
1981   PetscCall(DMGetField(cdm, 0, NULL, (PetscObject *)&fe));
1982   PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
1983   if (id != PETSCFE_CLASSID) PetscFunctionReturn(PETSC_SUCCESS);
1984   PetscCall(PetscFEGetBasisSpace(fe, &sp));
1985   PetscCall(PetscSpaceGetDegree(sp, &degree, NULL));
1986   for (PetscInt r = 0, rd = PetscCeilReal(((PetscReal)degree) / 2.); r < (PetscInt)PetscCeilReal(PetscLog2Real(degree)); ++r, rd = PetscCeilReal(((PetscReal)rd) / 2.)) {
1987     DM  cdm, rcdm;
1988     Mat In;
1989     Vec cl, rcl;
1990 
1991     PetscCall(DMRefine(odm, PetscObjectComm((PetscObject)odm), &rdm));
1992     PetscCall(DMPlexCreateCoordinateSpace(rdm, rd, PETSC_FALSE, NULL));
1993     PetscCall(PetscObjectSetName((PetscObject)rdm, "Refined Mesh with Linear Coordinates"));
1994     PetscCall(DMGetCoordinateDM(odm, &cdm));
1995     PetscCall(DMGetCoordinateDM(rdm, &rcdm));
1996     PetscCall(DMGetCoordinatesLocal(odm, &cl));
1997     PetscCall(DMGetCoordinatesLocal(rdm, &rcl));
1998     PetscCall(DMSetCoarseDM(rcdm, cdm));
1999     PetscCall(DMCreateInterpolation(cdm, rcdm, &In, NULL));
2000     PetscCall(MatMult(In, cl, rcl));
2001     PetscCall(MatDestroy(&In));
2002     PetscCall(DMSetCoordinatesLocal(rdm, rcl));
2003     PetscCall(DMDestroy(&odm));
2004     odm = rdm;
2005   }
2006   *hdm = rdm;
2007   PetscFunctionReturn(PETSC_SUCCESS);
2008 }
2009 
2010 #if defined(PETSC_HAVE_EXODUSII)
2011   #include <exodusII.h>
2012   #include <petscviewerexodusii.h>
2013 #endif
2014 
2015 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
2016 {
2017   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns, ispython;
2018   char      name[PETSC_MAX_PATH_LEN];
2019 
2020   PetscFunctionBegin;
2021   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2022   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2023   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
2024   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
2025   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2026   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
2027   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
2028   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
2029   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
2030   PetscCall(PetscObjectHasFunction((PetscObject)viewer, "PetscViewerPythonViewObject_C", &ispython));
2031   if (iascii) {
2032     PetscViewerFormat format;
2033     PetscCall(PetscViewerGetFormat(viewer, &format));
2034     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
2035     else PetscCall(DMPlexView_Ascii(dm, viewer));
2036   } else if (ishdf5) {
2037 #if defined(PETSC_HAVE_HDF5)
2038     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
2039 #else
2040     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2041 #endif
2042   } else if (isvtk) {
2043     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
2044   } else if (isdraw) {
2045     DM hdm;
2046 
2047     PetscCall(DMPlexCreateHighOrderSurrogate_Internal(dm, &hdm));
2048     PetscCall(DMPlexView_Draw(hdm, viewer));
2049     PetscCall(DMDestroy(&hdm));
2050   } else if (isglvis) {
2051     PetscCall(DMPlexView_GLVis(dm, viewer));
2052 #if defined(PETSC_HAVE_EXODUSII)
2053   } else if (isexodus) {
2054     /*
2055       exodusII requires that all sets be part of exactly one cell set.
2056       If the dm does not have a "Cell Sets" label defined, we create one
2057       with ID 1, containing all cells.
2058       Note that if the Cell Sets label is defined but does not cover all cells,
2059       we may still have a problem. This should probably be checked here or in the viewer;
2060     */
2061     PetscInt numCS;
2062     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
2063     if (!numCS) {
2064       PetscInt cStart, cEnd, c;
2065       PetscCall(DMCreateLabel(dm, "Cell Sets"));
2066       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
2067       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
2068     }
2069     PetscCall(DMView_PlexExodusII(dm, viewer));
2070 #endif
2071 #if defined(PETSC_HAVE_CGNS)
2072   } else if (iscgns) {
2073     PetscCall(DMView_PlexCGNS(dm, viewer));
2074 #endif
2075   } else if (ispython) {
2076     PetscCall(PetscViewerPythonViewObject(viewer, (PetscObject)dm));
2077   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
2078   /* Optionally view the partition */
2079   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
2080   if (flg) {
2081     Vec ranks;
2082     PetscCall(DMPlexCreateRankField(dm, &ranks));
2083     PetscCall(VecView(ranks, viewer));
2084     PetscCall(VecDestroy(&ranks));
2085   }
2086   /* Optionally view a label */
2087   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
2088   if (flg) {
2089     DMLabel label;
2090     Vec     val;
2091 
2092     PetscCall(DMGetLabel(dm, name, &label));
2093     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
2094     PetscCall(DMPlexCreateLabelField(dm, label, &val));
2095     PetscCall(VecView(val, viewer));
2096     PetscCall(VecDestroy(&val));
2097   }
2098   PetscFunctionReturn(PETSC_SUCCESS);
2099 }
2100 
2101 /*@
2102   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
2103 
2104   Collective
2105 
2106   Input Parameters:
2107 + dm     - The `DM` whose topology is to be saved
2108 - viewer - The `PetscViewer` to save it in
2109 
2110   Level: advanced
2111 
2112 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
2113 @*/
2114 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
2115 {
2116   PetscBool ishdf5;
2117 
2118   PetscFunctionBegin;
2119   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2120   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2121   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2122   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
2123   if (ishdf5) {
2124 #if defined(PETSC_HAVE_HDF5)
2125     PetscViewerFormat format;
2126     PetscCall(PetscViewerGetFormat(viewer, &format));
2127     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2128       IS globalPointNumbering;
2129 
2130       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2131       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
2132       PetscCall(ISDestroy(&globalPointNumbering));
2133     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2134 #else
2135     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2136 #endif
2137   }
2138   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
2139   PetscFunctionReturn(PETSC_SUCCESS);
2140 }
2141 
2142 /*@
2143   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
2144 
2145   Collective
2146 
2147   Input Parameters:
2148 + dm     - The `DM` whose coordinates are to be saved
2149 - viewer - The `PetscViewer` for saving
2150 
2151   Level: advanced
2152 
2153 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
2154 @*/
2155 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
2156 {
2157   PetscBool ishdf5;
2158 
2159   PetscFunctionBegin;
2160   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2161   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2162   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2163   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2164   if (ishdf5) {
2165 #if defined(PETSC_HAVE_HDF5)
2166     PetscViewerFormat format;
2167     PetscCall(PetscViewerGetFormat(viewer, &format));
2168     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2169       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
2170     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2171 #else
2172     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2173 #endif
2174   }
2175   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2176   PetscFunctionReturn(PETSC_SUCCESS);
2177 }
2178 
2179 /*@
2180   DMPlexLabelsView - Saves `DMPLEX` labels into a file
2181 
2182   Collective
2183 
2184   Input Parameters:
2185 + dm     - The `DM` whose labels are to be saved
2186 - viewer - The `PetscViewer` for saving
2187 
2188   Level: advanced
2189 
2190 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
2191 @*/
2192 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
2193 {
2194   PetscBool ishdf5;
2195 
2196   PetscFunctionBegin;
2197   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2198   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2199   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2200   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
2201   if (ishdf5) {
2202 #if defined(PETSC_HAVE_HDF5)
2203     IS                globalPointNumbering;
2204     PetscViewerFormat format;
2205 
2206     PetscCall(PetscViewerGetFormat(viewer, &format));
2207     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2208       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2209       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
2210       PetscCall(ISDestroy(&globalPointNumbering));
2211     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2212 #else
2213     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2214 #endif
2215   }
2216   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
2217   PetscFunctionReturn(PETSC_SUCCESS);
2218 }
2219 
2220 /*@
2221   DMPlexSectionView - Saves a section associated with a `DMPLEX`
2222 
2223   Collective
2224 
2225   Input Parameters:
2226 + dm        - The `DM` that contains the topology on which the section to be saved is defined
2227 . viewer    - The `PetscViewer` for saving
2228 - sectiondm - The `DM` that contains the section to be saved, can be `NULL`
2229 
2230   Level: advanced
2231 
2232   Notes:
2233   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.
2234 
2235   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.
2236 
2237 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
2238 @*/
2239 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
2240 {
2241   PetscBool ishdf5;
2242 
2243   PetscFunctionBegin;
2244   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2245   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2246   if (!sectiondm) sectiondm = dm;
2247   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2248   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2249   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
2250   if (ishdf5) {
2251 #if defined(PETSC_HAVE_HDF5)
2252     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
2253 #else
2254     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2255 #endif
2256   }
2257   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
2258   PetscFunctionReturn(PETSC_SUCCESS);
2259 }
2260 
2261 /*@
2262   DMPlexGlobalVectorView - Saves a global vector
2263 
2264   Collective
2265 
2266   Input Parameters:
2267 + dm        - The `DM` that represents the topology
2268 . viewer    - The `PetscViewer` to save data with
2269 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2270 - vec       - The global vector to be saved
2271 
2272   Level: advanced
2273 
2274   Notes:
2275   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.
2276 
2277   Calling sequence:
2278 .vb
2279        DMCreate(PETSC_COMM_WORLD, &dm);
2280        DMSetType(dm, DMPLEX);
2281        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2282        DMClone(dm, &sectiondm);
2283        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2284        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2285        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2286        PetscSectionSetChart(section, pStart, pEnd);
2287        PetscSectionSetUp(section);
2288        DMSetLocalSection(sectiondm, section);
2289        PetscSectionDestroy(&section);
2290        DMGetGlobalVector(sectiondm, &vec);
2291        PetscObjectSetName((PetscObject)vec, "vec_name");
2292        DMPlexTopologyView(dm, viewer);
2293        DMPlexSectionView(dm, viewer, sectiondm);
2294        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2295        DMRestoreGlobalVector(sectiondm, &vec);
2296        DMDestroy(&sectiondm);
2297        DMDestroy(&dm);
2298 .ve
2299 
2300 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2301 @*/
2302 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2303 {
2304   PetscBool ishdf5;
2305 
2306   PetscFunctionBegin;
2307   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2308   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2309   if (!sectiondm) sectiondm = dm;
2310   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2311   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2312   /* Check consistency */
2313   {
2314     PetscSection section;
2315     PetscBool    includesConstraints;
2316     PetscInt     m, m1;
2317 
2318     PetscCall(VecGetLocalSize(vec, &m1));
2319     PetscCall(DMGetGlobalSection(sectiondm, &section));
2320     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2321     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2322     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2323     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2324   }
2325   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2326   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2327   if (ishdf5) {
2328 #if defined(PETSC_HAVE_HDF5)
2329     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2330 #else
2331     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2332 #endif
2333   }
2334   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2335   PetscFunctionReturn(PETSC_SUCCESS);
2336 }
2337 
2338 /*@
2339   DMPlexLocalVectorView - Saves a local vector
2340 
2341   Collective
2342 
2343   Input Parameters:
2344 + dm        - The `DM` that represents the topology
2345 . viewer    - The `PetscViewer` to save data with
2346 . sectiondm - The `DM` that contains the local section on which `vec` is defined, can be `NULL`
2347 - vec       - The local vector to be saved
2348 
2349   Level: advanced
2350 
2351   Note:
2352   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.
2353 
2354   Calling sequence:
2355 .vb
2356        DMCreate(PETSC_COMM_WORLD, &dm);
2357        DMSetType(dm, DMPLEX);
2358        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2359        DMClone(dm, &sectiondm);
2360        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2361        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2362        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2363        PetscSectionSetChart(section, pStart, pEnd);
2364        PetscSectionSetUp(section);
2365        DMSetLocalSection(sectiondm, section);
2366        DMGetLocalVector(sectiondm, &vec);
2367        PetscObjectSetName((PetscObject)vec, "vec_name");
2368        DMPlexTopologyView(dm, viewer);
2369        DMPlexSectionView(dm, viewer, sectiondm);
2370        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2371        DMRestoreLocalVector(sectiondm, &vec);
2372        DMDestroy(&sectiondm);
2373        DMDestroy(&dm);
2374 .ve
2375 
2376 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2377 @*/
2378 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2379 {
2380   PetscBool ishdf5;
2381 
2382   PetscFunctionBegin;
2383   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2384   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2385   if (!sectiondm) sectiondm = dm;
2386   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2387   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2388   /* Check consistency */
2389   {
2390     PetscSection section;
2391     PetscBool    includesConstraints;
2392     PetscInt     m, m1;
2393 
2394     PetscCall(VecGetLocalSize(vec, &m1));
2395     PetscCall(DMGetLocalSection(sectiondm, &section));
2396     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2397     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2398     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2399     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2400   }
2401   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2402   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2403   if (ishdf5) {
2404 #if defined(PETSC_HAVE_HDF5)
2405     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2406 #else
2407     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2408 #endif
2409   }
2410   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2411   PetscFunctionReturn(PETSC_SUCCESS);
2412 }
2413 
2414 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2415 {
2416   PetscBool ishdf5;
2417 
2418   PetscFunctionBegin;
2419   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2420   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2421   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2422   if (ishdf5) {
2423 #if defined(PETSC_HAVE_HDF5)
2424     PetscViewerFormat format;
2425     PetscCall(PetscViewerGetFormat(viewer, &format));
2426     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2427       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2428     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2429       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2430     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2431     PetscFunctionReturn(PETSC_SUCCESS);
2432 #else
2433     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2434 #endif
2435   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2436 }
2437 
2438 /*@
2439   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2440 
2441   Collective
2442 
2443   Input Parameters:
2444 + dm     - The `DM` into which the topology is loaded
2445 - viewer - The `PetscViewer` for the saved topology
2446 
2447   Output Parameter:
2448 . 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;
2449   `NULL` if unneeded
2450 
2451   Level: advanced
2452 
2453 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2454           `PetscViewer`, `PetscSF`
2455 @*/
2456 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2457 {
2458   PetscBool ishdf5;
2459 
2460   PetscFunctionBegin;
2461   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2462   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2463   if (globalToLocalPointSF) PetscAssertPointer(globalToLocalPointSF, 3);
2464   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2465   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2466   if (ishdf5) {
2467 #if defined(PETSC_HAVE_HDF5)
2468     PetscViewerFormat format;
2469     PetscCall(PetscViewerGetFormat(viewer, &format));
2470     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2471       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2472     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2473 #else
2474     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2475 #endif
2476   }
2477   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2478   PetscFunctionReturn(PETSC_SUCCESS);
2479 }
2480 
2481 /*@
2482   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2483 
2484   Collective
2485 
2486   Input Parameters:
2487 + dm                   - The `DM` into which the coordinates are loaded
2488 . viewer               - The `PetscViewer` for the saved coordinates
2489 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2490 
2491   Level: advanced
2492 
2493 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2494           `PetscSF`, `PetscViewer`
2495 @*/
2496 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2497 {
2498   PetscBool ishdf5;
2499 
2500   PetscFunctionBegin;
2501   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2502   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2503   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2504   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2505   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2506   if (ishdf5) {
2507 #if defined(PETSC_HAVE_HDF5)
2508     PetscViewerFormat format;
2509     PetscCall(PetscViewerGetFormat(viewer, &format));
2510     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2511       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2512     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2513 #else
2514     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2515 #endif
2516   }
2517   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2518   PetscFunctionReturn(PETSC_SUCCESS);
2519 }
2520 
2521 /*@
2522   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2523 
2524   Collective
2525 
2526   Input Parameters:
2527 + dm                   - The `DM` into which the labels are loaded
2528 . viewer               - The `PetscViewer` for the saved labels
2529 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2530 
2531   Level: advanced
2532 
2533   Note:
2534   The `PetscSF` argument must not be `NULL` if the `DM` is distributed, otherwise an error occurs.
2535 
2536 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2537           `PetscSF`, `PetscViewer`
2538 @*/
2539 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2540 {
2541   PetscBool ishdf5;
2542 
2543   PetscFunctionBegin;
2544   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2545   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2546   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2547   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2548   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2549   if (ishdf5) {
2550 #if defined(PETSC_HAVE_HDF5)
2551     PetscViewerFormat format;
2552 
2553     PetscCall(PetscViewerGetFormat(viewer, &format));
2554     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2555       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2556     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2557 #else
2558     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2559 #endif
2560   }
2561   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2562   PetscFunctionReturn(PETSC_SUCCESS);
2563 }
2564 
2565 /*@
2566   DMPlexSectionLoad - Loads section into a `DMPLEX`
2567 
2568   Collective
2569 
2570   Input Parameters:
2571 + dm                   - The `DM` that represents the topology
2572 . viewer               - The `PetscViewer` that represents the on-disk section (sectionA)
2573 . sectiondm            - The `DM` into which the on-disk section (sectionA) is migrated, can be `NULL`
2574 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2575 
2576   Output Parameters:
2577 + 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)
2578 - 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)
2579 
2580   Level: advanced
2581 
2582   Notes:
2583   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.
2584 
2585   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.
2586 
2587   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.
2588 
2589   Example using 2 processes:
2590 .vb
2591   NX (number of points on dm): 4
2592   sectionA                   : the on-disk section
2593   vecA                       : a vector associated with sectionA
2594   sectionB                   : sectiondm's local section constructed in this function
2595   vecB (local)               : a vector associated with sectiondm's local section
2596   vecB (global)              : a vector associated with sectiondm's global section
2597 
2598                                      rank 0    rank 1
2599   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2600   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2601   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2602   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2603   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2604   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2605   sectionB->atlasDof             :     1 0 1 | 1 3
2606   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2607   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2608   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2609 .ve
2610   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2611 
2612 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2613 @*/
2614 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2615 {
2616   PetscBool ishdf5;
2617 
2618   PetscFunctionBegin;
2619   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2620   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2621   if (!sectiondm) sectiondm = dm;
2622   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2623   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2624   if (globalDofSF) PetscAssertPointer(globalDofSF, 5);
2625   if (localDofSF) PetscAssertPointer(localDofSF, 6);
2626   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2627   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2628   if (ishdf5) {
2629 #if defined(PETSC_HAVE_HDF5)
2630     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2631 #else
2632     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2633 #endif
2634   }
2635   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2636   PetscFunctionReturn(PETSC_SUCCESS);
2637 }
2638 
2639 /*@
2640   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2641 
2642   Collective
2643 
2644   Input Parameters:
2645 + dm        - The `DM` that represents the topology
2646 . viewer    - The `PetscViewer` that represents the on-disk vector data
2647 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2648 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2649 - vec       - The global vector to set values of
2650 
2651   Level: advanced
2652 
2653   Notes:
2654   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.
2655 
2656   Calling sequence:
2657 .vb
2658        DMCreate(PETSC_COMM_WORLD, &dm);
2659        DMSetType(dm, DMPLEX);
2660        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2661        DMPlexTopologyLoad(dm, viewer, &sfX);
2662        DMClone(dm, &sectiondm);
2663        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2664        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2665        DMGetGlobalVector(sectiondm, &vec);
2666        PetscObjectSetName((PetscObject)vec, "vec_name");
2667        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2668        DMRestoreGlobalVector(sectiondm, &vec);
2669        PetscSFDestroy(&gsf);
2670        PetscSFDestroy(&sfX);
2671        DMDestroy(&sectiondm);
2672        DMDestroy(&dm);
2673 .ve
2674 
2675 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2676           `PetscSF`, `PetscViewer`
2677 @*/
2678 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2679 {
2680   PetscBool ishdf5;
2681 
2682   PetscFunctionBegin;
2683   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2684   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2685   if (!sectiondm) sectiondm = dm;
2686   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2687   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2688   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2689   /* Check consistency */
2690   {
2691     PetscSection section;
2692     PetscBool    includesConstraints;
2693     PetscInt     m, m1;
2694 
2695     PetscCall(VecGetLocalSize(vec, &m1));
2696     PetscCall(DMGetGlobalSection(sectiondm, &section));
2697     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2698     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2699     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2700     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2701   }
2702   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2703   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2704   if (ishdf5) {
2705 #if defined(PETSC_HAVE_HDF5)
2706     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2707 #else
2708     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2709 #endif
2710   }
2711   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2712   PetscFunctionReturn(PETSC_SUCCESS);
2713 }
2714 
2715 /*@
2716   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2717 
2718   Collective
2719 
2720   Input Parameters:
2721 + dm        - The `DM` that represents the topology
2722 . viewer    - The `PetscViewer` that represents the on-disk vector data
2723 . sectiondm - The `DM` that contains the local section on which vec is defined, can be `NULL`
2724 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2725 - vec       - The local vector to set values of
2726 
2727   Level: advanced
2728 
2729   Notes:
2730   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.
2731 
2732   Calling sequence:
2733 .vb
2734        DMCreate(PETSC_COMM_WORLD, &dm);
2735        DMSetType(dm, DMPLEX);
2736        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2737        DMPlexTopologyLoad(dm, viewer, &sfX);
2738        DMClone(dm, &sectiondm);
2739        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2740        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2741        DMGetLocalVector(sectiondm, &vec);
2742        PetscObjectSetName((PetscObject)vec, "vec_name");
2743        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2744        DMRestoreLocalVector(sectiondm, &vec);
2745        PetscSFDestroy(&lsf);
2746        PetscSFDestroy(&sfX);
2747        DMDestroy(&sectiondm);
2748        DMDestroy(&dm);
2749 .ve
2750 
2751 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2752           `PetscSF`, `PetscViewer`
2753 @*/
2754 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2755 {
2756   PetscBool ishdf5;
2757 
2758   PetscFunctionBegin;
2759   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2760   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2761   if (!sectiondm) sectiondm = dm;
2762   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2763   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2764   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2765   /* Check consistency */
2766   {
2767     PetscSection section;
2768     PetscBool    includesConstraints;
2769     PetscInt     m, m1;
2770 
2771     PetscCall(VecGetLocalSize(vec, &m1));
2772     PetscCall(DMGetLocalSection(sectiondm, &section));
2773     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2774     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2775     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2776     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2777   }
2778   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2779   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2780   if (ishdf5) {
2781 #if defined(PETSC_HAVE_HDF5)
2782     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2783 #else
2784     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2785 #endif
2786   }
2787   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2788   PetscFunctionReturn(PETSC_SUCCESS);
2789 }
2790 
2791 PetscErrorCode DMDestroy_Plex(DM dm)
2792 {
2793   DM_Plex *mesh = (DM_Plex *)dm->data;
2794 
2795   PetscFunctionBegin;
2796   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2797   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2798   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2799   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2800   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", NULL));
2801   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2802   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2803   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2804   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2805   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2806   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2807   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetDefault_C", NULL));
2808   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetDefault_C", NULL));
2809   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetType_C", NULL));
2810   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetType_C", NULL));
2811   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2812   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2813   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetUseCeed_C", NULL));
2814   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetUseCeed_C", NULL));
2815   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2816   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2817   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2818   PetscCall(PetscFree(mesh->cones));
2819   PetscCall(PetscFree(mesh->coneOrientations));
2820   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2821   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2822   PetscCall(PetscFree(mesh->supports));
2823   PetscCall(PetscFree(mesh->cellTypes));
2824   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2825   PetscCall(PetscFree(mesh->tetgenOpts));
2826   PetscCall(PetscFree(mesh->triangleOpts));
2827   PetscCall(PetscFree(mesh->transformType));
2828   PetscCall(PetscFree(mesh->distributionName));
2829   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2830   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2831   PetscCall(ISDestroy(&mesh->subpointIS));
2832   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2833   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2834   if (mesh->periodic.face_sfs) {
2835     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFDestroy(&mesh->periodic.face_sfs[i]));
2836     PetscCall(PetscFree(mesh->periodic.face_sfs));
2837   }
2838   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2839   if (mesh->periodic.periodic_points) {
2840     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(ISDestroy(&mesh->periodic.periodic_points[i]));
2841     PetscCall(PetscFree(mesh->periodic.periodic_points));
2842   }
2843   if (mesh->periodic.transform) PetscCall(PetscFree(mesh->periodic.transform));
2844   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2845   PetscCall(ISDestroy(&mesh->anchorIS));
2846   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2847   PetscCall(PetscFree(mesh->parents));
2848   PetscCall(PetscFree(mesh->childIDs));
2849   PetscCall(PetscSectionDestroy(&mesh->childSection));
2850   PetscCall(PetscFree(mesh->children));
2851   PetscCall(DMDestroy(&mesh->referenceTree));
2852   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2853   PetscCall(PetscFree(mesh->neighbors));
2854   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2855   if (mesh->nonempty_comm != MPI_COMM_NULL && mesh->nonempty_comm != MPI_COMM_SELF) PetscCallMPI(MPI_Comm_free(&mesh->nonempty_comm));
2856   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2857   PetscCall(PetscFree(mesh));
2858   PetscFunctionReturn(PETSC_SUCCESS);
2859 }
2860 
2861 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2862 {
2863   PetscSection           sectionGlobal, sectionLocal;
2864   PetscInt               bs = -1, mbs;
2865   PetscInt               localSize, localStart = 0;
2866   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2867   MatType                mtype;
2868   ISLocalToGlobalMapping ltog;
2869 
2870   PetscFunctionBegin;
2871   PetscCall(MatInitializePackage());
2872   mtype = dm->mattype;
2873   PetscCall(DMGetLocalSection(dm, &sectionLocal));
2874   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2875   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2876   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2877   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2878   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2879   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2880   PetscCall(MatSetType(*J, mtype));
2881   PetscCall(MatSetFromOptions(*J));
2882   PetscCall(MatGetBlockSize(*J, &mbs));
2883   if (mbs > 1) bs = mbs;
2884   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2885   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2886   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2887   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2888   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2889   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2890   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2891   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2892   if (!isShell) {
2893     // There are three states with pblocks, since block starts can have no dofs:
2894     // UNKNOWN) New Block:   An open block has been signalled by pblocks[p] == 1
2895     // TRUE)    Block Start: The first entry in a block has been added
2896     // FALSE)   Block Add:   An additional block entry has been added, since pblocks[p] == 0
2897     PetscBT         blst;
2898     PetscBool3      bstate     = PETSC_BOOL3_UNKNOWN;
2899     PetscBool       fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2900     const PetscInt *perm       = NULL;
2901     PetscInt       *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2902     PetscInt        pStart, pEnd, dof, cdof, num_fields;
2903 
2904     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2905     PetscCall(PetscSectionGetBlockStarts(sectionLocal, &blst));
2906     if (sectionLocal->perm) PetscCall(ISGetIndices(sectionLocal->perm, &perm));
2907 
2908     PetscCall(PetscCalloc1(localSize, &pblocks));
2909     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2910     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2911     // We need to process in the permuted order to get block sizes right
2912     for (PetscInt point = pStart; point < pEnd; ++point) {
2913       const PetscInt p = perm ? perm[point] : point;
2914 
2915       switch (dm->blocking_type) {
2916       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2917         PetscInt bdof, offset;
2918 
2919         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2920         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2921         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2922         if (blst && PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_UNKNOWN;
2923         if (dof > 0) {
2924           // State change
2925           if (bstate == PETSC_BOOL3_UNKNOWN) bstate = PETSC_BOOL3_TRUE;
2926           else if (bstate == PETSC_BOOL3_TRUE && blst && !PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_FALSE;
2927 
2928           for (PetscInt i = 0; i < dof - cdof; ++i) pblocks[offset - localStart + i] = dof - cdof;
2929           // Signal block concatenation
2930           if (bstate == PETSC_BOOL3_FALSE && dof - cdof) pblocks[offset - localStart] = -(dof - cdof);
2931         }
2932         dof  = dof < 0 ? -(dof + 1) : dof;
2933         bdof = cdof && (dof - cdof) ? 1 : dof;
2934         if (dof) {
2935           if (bs < 0) {
2936             bs = bdof;
2937           } else if (bs != bdof) {
2938             bs = 1;
2939           }
2940         }
2941       } break;
2942       case DM_BLOCKING_FIELD_NODE: {
2943         for (PetscInt field = 0; field < num_fields; field++) {
2944           PetscInt num_comp, bdof, offset;
2945           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2946           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2947           if (dof < 0) continue;
2948           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2949           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2950           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);
2951           PetscInt num_nodes = dof / num_comp;
2952           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2953           // Handle possibly constant block size (unlikely)
2954           bdof = cdof && (dof - cdof) ? 1 : dof;
2955           if (dof) {
2956             if (bs < 0) {
2957               bs = bdof;
2958             } else if (bs != bdof) {
2959               bs = 1;
2960             }
2961           }
2962         }
2963       } break;
2964       }
2965     }
2966     if (sectionLocal->perm) PetscCall(ISRestoreIndices(sectionLocal->perm, &perm));
2967     /* Must have same blocksize on all procs (some might have no points) */
2968     bsLocal[0] = bs < 0 ? PETSC_INT_MAX : bs;
2969     bsLocal[1] = bs;
2970     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2971     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2972     else bs = bsMinMax[0];
2973     bs = PetscMax(1, bs);
2974     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2975     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2976       PetscCall(MatSetBlockSize(*J, bs));
2977       PetscCall(MatSetUp(*J));
2978     } else {
2979       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2980       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2981       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2982     }
2983     if (pblocks) { // Consolidate blocks
2984       PetscInt nblocks = 0;
2985       pblocks[0]       = PetscAbs(pblocks[0]);
2986       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2987         if (pblocks[i] == 0) continue;
2988         // Negative block size indicates the blocks should be concatenated
2989         if (pblocks[i] < 0) {
2990           pblocks[i] = -pblocks[i];
2991           pblocks[nblocks - 1] += pblocks[i];
2992         } else {
2993           pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2994         }
2995         for (PetscInt j = 1; j < pblocks[i]; j++)
2996           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);
2997       }
2998       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2999     }
3000     PetscCall(PetscFree(pblocks));
3001   }
3002   PetscCall(MatSetDM(*J, dm));
3003   PetscFunctionReturn(PETSC_SUCCESS);
3004 }
3005 
3006 /*@
3007   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
3008 
3009   Not Collective
3010 
3011   Input Parameter:
3012 . dm - The `DMPLEX`
3013 
3014   Output Parameter:
3015 . subsection - The subdomain section
3016 
3017   Level: developer
3018 
3019 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
3020 @*/
3021 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
3022 {
3023   DM_Plex *mesh = (DM_Plex *)dm->data;
3024 
3025   PetscFunctionBegin;
3026   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3027   if (!mesh->subdomainSection) {
3028     PetscSection section;
3029     PetscSF      sf;
3030 
3031     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
3032     PetscCall(DMGetLocalSection(dm, &section));
3033     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
3034     PetscCall(PetscSFDestroy(&sf));
3035   }
3036   *subsection = mesh->subdomainSection;
3037   PetscFunctionReturn(PETSC_SUCCESS);
3038 }
3039 
3040 /*@
3041   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
3042 
3043   Not Collective
3044 
3045   Input Parameter:
3046 . dm - The `DMPLEX`
3047 
3048   Output Parameters:
3049 + pStart - The first mesh point
3050 - pEnd   - The upper bound for mesh points
3051 
3052   Level: beginner
3053 
3054 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
3055 @*/
3056 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
3057 {
3058   DM_Plex *mesh = (DM_Plex *)dm->data;
3059 
3060   PetscFunctionBegin;
3061   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3062   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
3063   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
3064   PetscFunctionReturn(PETSC_SUCCESS);
3065 }
3066 
3067 /*@
3068   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
3069 
3070   Not Collective
3071 
3072   Input Parameters:
3073 + dm     - The `DMPLEX`
3074 . pStart - The first mesh point
3075 - pEnd   - The upper bound for mesh points
3076 
3077   Level: beginner
3078 
3079 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
3080 @*/
3081 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
3082 {
3083   DM_Plex *mesh = (DM_Plex *)dm->data;
3084 
3085   PetscFunctionBegin;
3086   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3087   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
3088   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
3089   PetscCall(PetscFree(mesh->cellTypes));
3090   PetscFunctionReturn(PETSC_SUCCESS);
3091 }
3092 
3093 /*@
3094   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
3095 
3096   Not Collective
3097 
3098   Input Parameters:
3099 + dm - The `DMPLEX`
3100 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3101 
3102   Output Parameter:
3103 . size - The cone size for point `p`
3104 
3105   Level: beginner
3106 
3107 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3108 @*/
3109 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
3110 {
3111   DM_Plex *mesh = (DM_Plex *)dm->data;
3112 
3113   PetscFunctionBegin;
3114   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3115   PetscAssertPointer(size, 3);
3116   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
3117   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
3118   PetscFunctionReturn(PETSC_SUCCESS);
3119 }
3120 
3121 /*@
3122   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
3123 
3124   Not Collective
3125 
3126   Input Parameters:
3127 + dm   - The `DMPLEX`
3128 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3129 - size - The cone size for point `p`
3130 
3131   Level: beginner
3132 
3133   Note:
3134   This should be called after `DMPlexSetChart()`.
3135 
3136 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
3137 @*/
3138 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
3139 {
3140   DM_Plex *mesh = (DM_Plex *)dm->data;
3141 
3142   PetscFunctionBegin;
3143   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3144   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
3145   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
3146   PetscFunctionReturn(PETSC_SUCCESS);
3147 }
3148 
3149 /*@C
3150   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
3151 
3152   Not Collective
3153 
3154   Input Parameters:
3155 + dm - The `DMPLEX`
3156 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3157 
3158   Output Parameter:
3159 . cone - An array of points which are on the in-edges for point `p`, the length of `cone` is the result of `DMPlexGetConeSize()`
3160 
3161   Level: beginner
3162 
3163   Fortran Notes:
3164   `cone` must be declared with
3165 .vb
3166   PetscInt, pointer :: cone(:)
3167 .ve
3168 
3169   You must also call `DMPlexRestoreCone()` after you finish using the array.
3170   `DMPlexRestoreCone()` is not needed/available in C.
3171 
3172 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
3173 @*/
3174 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
3175 {
3176   DM_Plex *mesh = (DM_Plex *)dm->data;
3177   PetscInt off;
3178 
3179   PetscFunctionBegin;
3180   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3181   PetscAssertPointer(cone, 3);
3182   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3183   *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3184   PetscFunctionReturn(PETSC_SUCCESS);
3185 }
3186 
3187 /*@
3188   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
3189 
3190   Not Collective
3191 
3192   Input Parameters:
3193 + dm - The `DMPLEX`
3194 - p  - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3195 
3196   Output Parameters:
3197 + pConesSection - `PetscSection` describing the layout of `pCones`
3198 - pCones        - An `IS` containing the points which are on the in-edges for the point set `p`
3199 
3200   Level: intermediate
3201 
3202 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
3203 @*/
3204 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
3205 {
3206   PetscSection cs, newcs;
3207   PetscInt    *cones;
3208   PetscInt    *newarr = NULL;
3209   PetscInt     n;
3210 
3211   PetscFunctionBegin;
3212   PetscCall(DMPlexGetCones(dm, &cones));
3213   PetscCall(DMPlexGetConeSection(dm, &cs));
3214   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
3215   if (pConesSection) *pConesSection = newcs;
3216   if (pCones) {
3217     PetscCall(PetscSectionGetStorageSize(newcs, &n));
3218     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
3219   }
3220   PetscFunctionReturn(PETSC_SUCCESS);
3221 }
3222 
3223 /*@
3224   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
3225 
3226   Not Collective
3227 
3228   Input Parameters:
3229 + dm     - The `DMPLEX`
3230 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3231 
3232   Output Parameter:
3233 . expandedPoints - An `IS` containing the of vertices recursively expanded from input points
3234 
3235   Level: advanced
3236 
3237   Notes:
3238   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
3239 
3240   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
3241 
3242 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
3243           `DMPlexGetDepth()`, `IS`
3244 @*/
3245 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
3246 {
3247   IS      *expandedPointsAll;
3248   PetscInt depth;
3249 
3250   PetscFunctionBegin;
3251   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3252   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3253   PetscAssertPointer(expandedPoints, 3);
3254   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3255   *expandedPoints = expandedPointsAll[0];
3256   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
3257   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3258   PetscFunctionReturn(PETSC_SUCCESS);
3259 }
3260 
3261 /*@
3262   DMPlexGetConeRecursive - Expand each given point into its cone points and do that recursively until we end up just with vertices
3263   (DAG points of depth 0, i.e., without cones).
3264 
3265   Not Collective
3266 
3267   Input Parameters:
3268 + dm     - The `DMPLEX`
3269 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3270 
3271   Output Parameters:
3272 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3273 . expandedPoints - (optional) An array of index sets with recursively expanded cones
3274 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3275 
3276   Level: advanced
3277 
3278   Notes:
3279   Like `DMPlexGetConeTuple()` but recursive.
3280 
3281   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.
3282   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
3283 
3284   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\:
3285   (1) DAG points in `expandedPoints`[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
3286   (2) DAG points in `expandedPoints`[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
3287 
3288 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3289           `DMPlexGetDepth()`, `PetscSection`, `IS`
3290 @*/
3291 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3292 {
3293   const PetscInt *arr0 = NULL, *cone = NULL;
3294   PetscInt       *arr = NULL, *newarr = NULL;
3295   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
3296   IS             *expandedPoints_;
3297   PetscSection   *sections_;
3298 
3299   PetscFunctionBegin;
3300   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3301   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3302   if (depth) PetscAssertPointer(depth, 3);
3303   if (expandedPoints) PetscAssertPointer(expandedPoints, 4);
3304   if (sections) PetscAssertPointer(sections, 5);
3305   PetscCall(ISGetLocalSize(points, &n));
3306   PetscCall(ISGetIndices(points, &arr0));
3307   PetscCall(DMPlexGetDepth(dm, &depth_));
3308   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
3309   PetscCall(PetscCalloc1(depth_, &sections_));
3310   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
3311   for (d = depth_ - 1; d >= 0; d--) {
3312     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
3313     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
3314     for (i = 0; i < n; i++) {
3315       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
3316       if (arr[i] >= start && arr[i] < end) {
3317         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
3318         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
3319       } else {
3320         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
3321       }
3322     }
3323     PetscCall(PetscSectionSetUp(sections_[d]));
3324     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
3325     PetscCall(PetscMalloc1(newn, &newarr));
3326     for (i = 0; i < n; i++) {
3327       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
3328       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
3329       if (cn > 1) {
3330         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
3331         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3332       } else {
3333         newarr[co] = arr[i];
3334       }
3335     }
3336     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3337     arr = newarr;
3338     n   = newn;
3339   }
3340   PetscCall(ISRestoreIndices(points, &arr0));
3341   *depth = depth_;
3342   if (expandedPoints) *expandedPoints = expandedPoints_;
3343   else {
3344     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3345     PetscCall(PetscFree(expandedPoints_));
3346   }
3347   if (sections) *sections = sections_;
3348   else {
3349     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3350     PetscCall(PetscFree(sections_));
3351   }
3352   PetscFunctionReturn(PETSC_SUCCESS);
3353 }
3354 
3355 /*@
3356   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3357 
3358   Not Collective
3359 
3360   Input Parameters:
3361 + dm     - The `DMPLEX`
3362 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3363 
3364   Output Parameters:
3365 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3366 . expandedPoints - (optional) An array of recursively expanded cones
3367 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3368 
3369   Level: advanced
3370 
3371   Note:
3372   See `DMPlexGetConeRecursive()`
3373 
3374 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3375           `DMPlexGetDepth()`, `IS`, `PetscSection`
3376 @*/
3377 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3378 {
3379   PetscInt d, depth_;
3380 
3381   PetscFunctionBegin;
3382   PetscCall(DMPlexGetDepth(dm, &depth_));
3383   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3384   if (depth) *depth = 0;
3385   if (expandedPoints) {
3386     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&(*expandedPoints)[d]));
3387     PetscCall(PetscFree(*expandedPoints));
3388   }
3389   if (sections) {
3390     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&(*sections)[d]));
3391     PetscCall(PetscFree(*sections));
3392   }
3393   PetscFunctionReturn(PETSC_SUCCESS);
3394 }
3395 
3396 /*@
3397   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
3398 
3399   Not Collective
3400 
3401   Input Parameters:
3402 + dm   - The `DMPLEX`
3403 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3404 - cone - An array of points which are on the in-edges for point `p`, its length must have been previously provided with `DMPlexSetConeSize()`
3405 
3406   Level: beginner
3407 
3408   Note:
3409   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3410 
3411 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3412 @*/
3413 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3414 {
3415   DM_Plex *mesh = (DM_Plex *)dm->data;
3416   PetscInt dof, off, c;
3417 
3418   PetscFunctionBegin;
3419   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3420   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3421   if (dof) PetscAssertPointer(cone, 3);
3422   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3423   if (PetscDefined(USE_DEBUG)) {
3424     PetscInt pStart, pEnd;
3425     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3426     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);
3427     for (c = 0; c < dof; ++c) {
3428       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);
3429       mesh->cones[off + c] = cone[c];
3430     }
3431   } else {
3432     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3433   }
3434   PetscFunctionReturn(PETSC_SUCCESS);
3435 }
3436 
3437 /*@C
3438   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3439 
3440   Not Collective
3441 
3442   Input Parameters:
3443 + dm - The `DMPLEX`
3444 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3445 
3446   Output Parameter:
3447 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3448                     integer giving the prescription for cone traversal. Its length is given by the result of `DMPlexSetConeSize()`
3449 
3450   Level: beginner
3451 
3452   Note:
3453   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3454   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3455   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3456   with the identity.
3457 
3458   Fortran Notes:
3459   You must call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3460   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3461 
3462 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetConeSize()`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`,
3463           `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3464 @*/
3465 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3466 {
3467   DM_Plex *mesh = (DM_Plex *)dm->data;
3468   PetscInt off;
3469 
3470   PetscFunctionBegin;
3471   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3472   if (PetscDefined(USE_DEBUG)) {
3473     PetscInt dof;
3474     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3475     if (dof) PetscAssertPointer(coneOrientation, 3);
3476   }
3477   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3478 
3479   *coneOrientation = &mesh->coneOrientations[off];
3480   PetscFunctionReturn(PETSC_SUCCESS);
3481 }
3482 
3483 /*@
3484   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3485 
3486   Not Collective
3487 
3488   Input Parameters:
3489 + dm              - The `DMPLEX`
3490 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3491 - coneOrientation - An array of orientations. Its length is given by the result of `DMPlexSetConeSize()`
3492 
3493   Level: beginner
3494 
3495   Notes:
3496   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3497 
3498   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3499 
3500 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3501 @*/
3502 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3503 {
3504   DM_Plex *mesh = (DM_Plex *)dm->data;
3505   PetscInt pStart, pEnd;
3506   PetscInt dof, off, c;
3507 
3508   PetscFunctionBegin;
3509   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3510   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3511   if (dof) PetscAssertPointer(coneOrientation, 3);
3512   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3513   if (PetscDefined(USE_DEBUG)) {
3514     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3515     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);
3516     for (c = 0; c < dof; ++c) {
3517       PetscInt cdof, o = coneOrientation[c];
3518 
3519       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3520       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);
3521       mesh->coneOrientations[off + c] = o;
3522     }
3523   } else {
3524     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3525   }
3526   PetscFunctionReturn(PETSC_SUCCESS);
3527 }
3528 
3529 /*@
3530   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3531 
3532   Not Collective
3533 
3534   Input Parameters:
3535 + dm        - The `DMPLEX`
3536 . p         - The point, which must lie in the chart set with `DMPlexSetChart()`
3537 . conePos   - The local index in the cone where the point should be put
3538 - conePoint - The mesh point to insert
3539 
3540   Level: beginner
3541 
3542 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3543 @*/
3544 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3545 {
3546   DM_Plex *mesh = (DM_Plex *)dm->data;
3547   PetscInt pStart, pEnd;
3548   PetscInt dof, off;
3549 
3550   PetscFunctionBegin;
3551   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3552   if (PetscDefined(USE_DEBUG)) {
3553     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3554     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);
3555     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);
3556     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3557     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);
3558   }
3559   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3560   mesh->cones[off + conePos] = conePoint;
3561   PetscFunctionReturn(PETSC_SUCCESS);
3562 }
3563 
3564 /*@
3565   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3566 
3567   Not Collective
3568 
3569   Input Parameters:
3570 + dm              - The `DMPLEX`
3571 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3572 . conePos         - The local index in the cone where the point should be put
3573 - coneOrientation - The point orientation to insert
3574 
3575   Level: beginner
3576 
3577   Note:
3578   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3579 
3580 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3581 @*/
3582 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3583 {
3584   DM_Plex *mesh = (DM_Plex *)dm->data;
3585   PetscInt pStart, pEnd;
3586   PetscInt dof, off;
3587 
3588   PetscFunctionBegin;
3589   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3590   if (PetscDefined(USE_DEBUG)) {
3591     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3592     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);
3593     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3594     PetscCheck(!(conePos < 0) && !(conePos >= dof), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone position %" PetscInt_FMT " of point %" PetscInt_FMT " is not in the valid range [0, %" PetscInt_FMT ")", conePos, p, dof);
3595   }
3596   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3597   mesh->coneOrientations[off + conePos] = coneOrientation;
3598   PetscFunctionReturn(PETSC_SUCCESS);
3599 }
3600 
3601 /*@C
3602   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3603 
3604   Not collective
3605 
3606   Input Parameters:
3607 + dm - The DMPlex
3608 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3609 
3610   Output Parameters:
3611 + cone - An array of points which are on the in-edges for point `p`
3612 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3613          integer giving the prescription for cone traversal.
3614 
3615   Level: beginner
3616 
3617   Notes:
3618   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3619   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3620   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3621   with the identity.
3622 
3623   You must also call `DMPlexRestoreOrientedCone()` after you finish using the returned array.
3624 
3625   Fortran Notes:
3626   `cone` and `ornt` must be declared with
3627 .vb
3628   PetscInt, pointer :: cone(:)
3629   PetscInt, pointer :: ornt(:)
3630 .ve
3631 
3632 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3633 @*/
3634 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3635 {
3636   DM_Plex *mesh = (DM_Plex *)dm->data;
3637 
3638   PetscFunctionBegin;
3639   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3640   if (mesh->tr) {
3641     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3642   } else {
3643     PetscInt off;
3644     if (PetscDefined(USE_DEBUG)) {
3645       PetscInt dof;
3646       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3647       if (dof) {
3648         if (cone) PetscAssertPointer(cone, 3);
3649         if (ornt) PetscAssertPointer(ornt, 4);
3650       }
3651     }
3652     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3653     if (cone) *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3654     if (ornt) *ornt = PetscSafePointerPlusOffset(mesh->coneOrientations, off);
3655   }
3656   PetscFunctionReturn(PETSC_SUCCESS);
3657 }
3658 
3659 /*@C
3660   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG obtained with `DMPlexGetOrientedCone()`
3661 
3662   Not Collective
3663 
3664   Input Parameters:
3665 + dm   - The DMPlex
3666 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3667 . cone - An array of points which are on the in-edges for point p
3668 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3669          integer giving the prescription for cone traversal.
3670 
3671   Level: beginner
3672 
3673 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3674 @*/
3675 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3676 {
3677   DM_Plex *mesh = (DM_Plex *)dm->data;
3678 
3679   PetscFunctionBegin;
3680   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3681   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3682   PetscFunctionReturn(PETSC_SUCCESS);
3683 }
3684 
3685 /*@
3686   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3687 
3688   Not Collective
3689 
3690   Input Parameters:
3691 + dm - The `DMPLEX`
3692 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3693 
3694   Output Parameter:
3695 . size - The support size for point `p`
3696 
3697   Level: beginner
3698 
3699 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3700 @*/
3701 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3702 {
3703   DM_Plex *mesh = (DM_Plex *)dm->data;
3704 
3705   PetscFunctionBegin;
3706   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3707   PetscAssertPointer(size, 3);
3708   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3709   PetscFunctionReturn(PETSC_SUCCESS);
3710 }
3711 
3712 /*@
3713   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3714 
3715   Not Collective
3716 
3717   Input Parameters:
3718 + dm   - The `DMPLEX`
3719 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3720 - size - The support size for point `p`
3721 
3722   Level: beginner
3723 
3724   Note:
3725   This should be called after `DMPlexSetChart()`.
3726 
3727 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3728 @*/
3729 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3730 {
3731   DM_Plex *mesh = (DM_Plex *)dm->data;
3732 
3733   PetscFunctionBegin;
3734   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3735   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3736   PetscFunctionReturn(PETSC_SUCCESS);
3737 }
3738 
3739 /*@C
3740   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3741 
3742   Not Collective
3743 
3744   Input Parameters:
3745 + dm - The `DMPLEX`
3746 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3747 
3748   Output Parameter:
3749 . support - An array of points which are on the out-edges for point `p`, its length is that obtained from `DMPlexGetSupportSize()`
3750 
3751   Level: beginner
3752 
3753   Fortran Notes:
3754   `support` must be declared with
3755 .vb
3756   PetscInt, pointer :: support(:)
3757 .ve
3758 
3759   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3760   `DMPlexRestoreSupport()` is not needed/available in C.
3761 
3762 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3763 @*/
3764 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3765 {
3766   DM_Plex *mesh = (DM_Plex *)dm->data;
3767   PetscInt off;
3768 
3769   PetscFunctionBegin;
3770   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3771   PetscAssertPointer(support, 3);
3772   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3773   *support = PetscSafePointerPlusOffset(mesh->supports, off);
3774   PetscFunctionReturn(PETSC_SUCCESS);
3775 }
3776 
3777 /*@
3778   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3779 
3780   Not Collective
3781 
3782   Input Parameters:
3783 + dm      - The `DMPLEX`
3784 . p       - The point, which must lie in the chart set with `DMPlexSetChart()`
3785 - support - An array of points which are on the out-edges for point `p`, its length is that obtained from `DMPlexGetSupportSize()`
3786 
3787   Level: beginner
3788 
3789   Note:
3790   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3791 
3792 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3793 @*/
3794 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3795 {
3796   DM_Plex *mesh = (DM_Plex *)dm->data;
3797   PetscInt pStart, pEnd;
3798   PetscInt dof, off, c;
3799 
3800   PetscFunctionBegin;
3801   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3802   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3803   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3804   if (dof) PetscAssertPointer(support, 3);
3805   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3806   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);
3807   for (c = 0; c < dof; ++c) {
3808     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);
3809     mesh->supports[off + c] = support[c];
3810   }
3811   PetscFunctionReturn(PETSC_SUCCESS);
3812 }
3813 
3814 /*@
3815   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3816 
3817   Not Collective
3818 
3819   Input Parameters:
3820 + dm           - The `DMPLEX`
3821 . p            - The point, which must lie in the chart set with `DMPlexSetChart()`
3822 . supportPos   - The local index in the cone where the point should be put
3823 - supportPoint - The mesh point to insert
3824 
3825   Level: beginner
3826 
3827 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3828 @*/
3829 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3830 {
3831   DM_Plex *mesh = (DM_Plex *)dm->data;
3832   PetscInt pStart, pEnd;
3833   PetscInt dof, off;
3834 
3835   PetscFunctionBegin;
3836   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3837   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3838   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3839   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3840   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);
3841   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);
3842   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);
3843   mesh->supports[off + supportPos] = supportPoint;
3844   PetscFunctionReturn(PETSC_SUCCESS);
3845 }
3846 
3847 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3848 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3849 {
3850   switch (ct) {
3851   case DM_POLYTOPE_SEGMENT:
3852     if (o == -1) return -2;
3853     break;
3854   case DM_POLYTOPE_TRIANGLE:
3855     if (o == -3) return -1;
3856     if (o == -2) return -3;
3857     if (o == -1) return -2;
3858     break;
3859   case DM_POLYTOPE_QUADRILATERAL:
3860     if (o == -4) return -2;
3861     if (o == -3) return -1;
3862     if (o == -2) return -4;
3863     if (o == -1) return -3;
3864     break;
3865   default:
3866     return o;
3867   }
3868   return o;
3869 }
3870 
3871 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3872 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3873 {
3874   switch (ct) {
3875   case DM_POLYTOPE_SEGMENT:
3876     if ((o == -2) || (o == 1)) return -1;
3877     if (o == -1) return 0;
3878     break;
3879   case DM_POLYTOPE_TRIANGLE:
3880     if (o == -3) return -2;
3881     if (o == -2) return -1;
3882     if (o == -1) return -3;
3883     break;
3884   case DM_POLYTOPE_QUADRILATERAL:
3885     if (o == -4) return -2;
3886     if (o == -3) return -1;
3887     if (o == -2) return -4;
3888     if (o == -1) return -3;
3889     break;
3890   default:
3891     return o;
3892   }
3893   return o;
3894 }
3895 
3896 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3897 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3898 {
3899   PetscInt pStart, pEnd, p;
3900 
3901   PetscFunctionBegin;
3902   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3903   for (p = pStart; p < pEnd; ++p) {
3904     const PetscInt *cone, *ornt;
3905     PetscInt        coneSize, c;
3906 
3907     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3908     PetscCall(DMPlexGetCone(dm, p, &cone));
3909     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3910     for (c = 0; c < coneSize; ++c) {
3911       DMPolytopeType ct;
3912       const PetscInt o = ornt[c];
3913 
3914       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3915       switch (ct) {
3916       case DM_POLYTOPE_SEGMENT:
3917         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3918         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3919         break;
3920       case DM_POLYTOPE_TRIANGLE:
3921         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3922         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3923         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3924         break;
3925       case DM_POLYTOPE_QUADRILATERAL:
3926         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3927         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3928         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3929         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3930         break;
3931       default:
3932         break;
3933       }
3934     }
3935   }
3936   PetscFunctionReturn(PETSC_SUCCESS);
3937 }
3938 
3939 static inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3940 {
3941   DM_Plex *mesh = (DM_Plex *)dm->data;
3942 
3943   PetscFunctionBeginHot;
3944   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3945     if (useCone) {
3946       PetscCall(DMPlexGetConeSize(dm, p, size));
3947       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3948     } else {
3949       PetscCall(DMPlexGetSupportSize(dm, p, size));
3950       PetscCall(DMPlexGetSupport(dm, p, arr));
3951     }
3952   } else {
3953     if (useCone) {
3954       const PetscSection s   = mesh->coneSection;
3955       const PetscInt     ps  = p - s->pStart;
3956       const PetscInt     off = s->atlasOff[ps];
3957 
3958       *size = s->atlasDof[ps];
3959       *arr  = mesh->cones + off;
3960       *ornt = mesh->coneOrientations + off;
3961     } else {
3962       const PetscSection s   = mesh->supportSection;
3963       const PetscInt     ps  = p - s->pStart;
3964       const PetscInt     off = s->atlasOff[ps];
3965 
3966       *size = s->atlasDof[ps];
3967       *arr  = mesh->supports + off;
3968     }
3969   }
3970   PetscFunctionReturn(PETSC_SUCCESS);
3971 }
3972 
3973 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3974 {
3975   DM_Plex *mesh = (DM_Plex *)dm->data;
3976 
3977   PetscFunctionBeginHot;
3978   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3979     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
3980   }
3981   PetscFunctionReturn(PETSC_SUCCESS);
3982 }
3983 
3984 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3985 {
3986   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3987   PetscInt       *closure;
3988   const PetscInt *tmp = NULL, *tmpO = NULL;
3989   PetscInt        off = 0, tmpSize, t;
3990 
3991   PetscFunctionBeginHot;
3992   if (ornt) {
3993     PetscCall(DMPlexGetCellType(dm, p, &ct));
3994     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;
3995   }
3996   if (*points) {
3997     closure = *points;
3998   } else {
3999     PetscInt maxConeSize, maxSupportSize;
4000     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
4001     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
4002   }
4003   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
4004   if (ct == DM_POLYTOPE_UNKNOWN) {
4005     closure[off++] = p;
4006     closure[off++] = 0;
4007     for (t = 0; t < tmpSize; ++t) {
4008       closure[off++] = tmp[t];
4009       closure[off++] = tmpO ? tmpO[t] : 0;
4010     }
4011   } else {
4012     const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, ornt);
4013 
4014     /* We assume that cells with a valid type have faces with a valid type */
4015     closure[off++] = p;
4016     closure[off++] = ornt;
4017     for (t = 0; t < tmpSize; ++t) {
4018       DMPolytopeType ft;
4019 
4020       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
4021       closure[off++] = tmp[arr[t]];
4022       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
4023     }
4024   }
4025   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
4026   if (numPoints) *numPoints = tmpSize + 1;
4027   if (points) *points = closure;
4028   PetscFunctionReturn(PETSC_SUCCESS);
4029 }
4030 
4031 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
4032 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
4033 {
4034   const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, o);
4035   const PetscInt *cone, *ornt;
4036   PetscInt       *pts, *closure = NULL;
4037   DMPolytopeType  ft;
4038   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
4039   PetscInt        dim, coneSize, c, d, clSize, cl;
4040 
4041   PetscFunctionBeginHot;
4042   PetscCall(DMGetDimension(dm, &dim));
4043   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
4044   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
4045   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
4046   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
4047   maxSize       = PetscMax(coneSeries, supportSeries);
4048   if (*points) {
4049     pts = *points;
4050   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
4051   c        = 0;
4052   pts[c++] = point;
4053   pts[c++] = o;
4054   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
4055   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
4056   for (cl = 0; cl < clSize * 2; cl += 2) {
4057     pts[c++] = closure[cl];
4058     pts[c++] = closure[cl + 1];
4059   }
4060   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
4061   for (cl = 0; cl < clSize * 2; cl += 2) {
4062     pts[c++] = closure[cl];
4063     pts[c++] = closure[cl + 1];
4064   }
4065   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
4066   for (d = 2; d < coneSize; ++d) {
4067     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
4068     pts[c++] = cone[arr[d * 2 + 0]];
4069     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
4070   }
4071   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
4072   if (dim >= 3) {
4073     for (d = 2; d < coneSize; ++d) {
4074       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
4075       const PetscInt *fcone, *fornt;
4076       PetscInt        fconeSize, fc, i;
4077 
4078       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
4079       const PetscInt *farr = DMPolytopeTypeGetArrangement(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
4080       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
4081       for (fc = 0; fc < fconeSize; ++fc) {
4082         const PetscInt cp = fcone[farr[fc * 2 + 0]];
4083         const PetscInt co = farr[fc * 2 + 1];
4084 
4085         for (i = 0; i < c; i += 2)
4086           if (pts[i] == cp) break;
4087         if (i == c) {
4088           PetscCall(DMPlexGetCellType(dm, cp, &ft));
4089           pts[c++] = cp;
4090           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
4091         }
4092       }
4093       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
4094     }
4095   }
4096   *numPoints = c / 2;
4097   *points    = pts;
4098   PetscFunctionReturn(PETSC_SUCCESS);
4099 }
4100 
4101 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4102 {
4103   DMPolytopeType ct;
4104   PetscInt      *closure, *fifo;
4105   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
4106   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
4107   PetscInt       depth, maxSize;
4108 
4109   PetscFunctionBeginHot;
4110   PetscCall(DMPlexGetDepth(dm, &depth));
4111   if (depth == 1) {
4112     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
4113     PetscFunctionReturn(PETSC_SUCCESS);
4114   }
4115   PetscCall(DMPlexGetCellType(dm, p, &ct));
4116   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;
4117   if (DMPolytopeTypeIsHybrid(ct) && ct != DM_POLYTOPE_POINT_PRISM_TENSOR) {
4118     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
4119     PetscFunctionReturn(PETSC_SUCCESS);
4120   }
4121   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
4122   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
4123   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
4124   maxSize       = PetscMax(coneSeries, supportSeries);
4125   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4126   if (*points) {
4127     closure = *points;
4128   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
4129   closure[closureSize++] = p;
4130   closure[closureSize++] = ornt;
4131   fifo[fifoSize++]       = p;
4132   fifo[fifoSize++]       = ornt;
4133   fifo[fifoSize++]       = ct;
4134   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
4135   while (fifoSize - fifoStart) {
4136     const PetscInt       q    = fifo[fifoStart++];
4137     const PetscInt       o    = fifo[fifoStart++];
4138     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
4139     const PetscInt      *qarr = DMPolytopeTypeGetArrangement(qt, o);
4140     const PetscInt      *tmp, *tmpO = NULL;
4141     PetscInt             tmpSize, t;
4142 
4143     if (PetscDefined(USE_DEBUG)) {
4144       PetscInt nO = DMPolytopeTypeGetNumArrangements(qt) / 2;
4145       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);
4146     }
4147     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4148     for (t = 0; t < tmpSize; ++t) {
4149       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
4150       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
4151       const PetscInt cp = tmp[ip];
4152       PetscCall(DMPlexGetCellType(dm, cp, &ct));
4153       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
4154       PetscInt       c;
4155 
4156       /* Check for duplicate */
4157       for (c = 0; c < closureSize; c += 2) {
4158         if (closure[c] == cp) break;
4159       }
4160       if (c == closureSize) {
4161         closure[closureSize++] = cp;
4162         closure[closureSize++] = co;
4163         fifo[fifoSize++]       = cp;
4164         fifo[fifoSize++]       = co;
4165         fifo[fifoSize++]       = ct;
4166       }
4167     }
4168     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4169   }
4170   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4171   if (numPoints) *numPoints = closureSize / 2;
4172   if (points) *points = closure;
4173   PetscFunctionReturn(PETSC_SUCCESS);
4174 }
4175 
4176 /*@C
4177   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
4178 
4179   Not Collective
4180 
4181   Input Parameters:
4182 + dm      - The `DMPLEX`
4183 . p       - The mesh point
4184 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
4185 
4186   Input/Output Parameter:
4187 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
4188            if *points is `NULL` on input, internal storage will be returned, use `DMPlexRestoreTransitiveClosure()`,
4189            otherwise the provided array is used to hold the values
4190 
4191   Output Parameter:
4192 . numPoints - The number of points in the closure, so `points` is of size 2*`numPoints`
4193 
4194   Level: beginner
4195 
4196   Note:
4197   If using internal storage (points is `NULL` on input), each call overwrites the last output.
4198 
4199   Fortran Notes:
4200   `points` must be declared with
4201 .vb
4202   PetscInt, pointer :: points(:)
4203 .ve
4204   and is always allocated by the function.
4205 
4206   The `numPoints` argument is not present in the Fortran binding.
4207 
4208 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4209 @*/
4210 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4211 {
4212   PetscFunctionBeginHot;
4213   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4214   if (numPoints) PetscAssertPointer(numPoints, 4);
4215   if (points) PetscAssertPointer(points, 5);
4216   if (PetscDefined(USE_DEBUG)) {
4217     PetscInt pStart, pEnd;
4218     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4219     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);
4220   }
4221   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
4222   PetscFunctionReturn(PETSC_SUCCESS);
4223 }
4224 
4225 /*@C
4226   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
4227 
4228   Not Collective
4229 
4230   Input Parameters:
4231 + dm        - The `DMPLEX`
4232 . p         - The mesh point
4233 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
4234 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4235 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
4236 
4237   Level: beginner
4238 
4239   Note:
4240   If not using internal storage (points is not `NULL` on input), this call is unnecessary
4241 
4242 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4243 @*/
4244 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4245 {
4246   PetscFunctionBeginHot;
4247   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4248   if (numPoints) *numPoints = 0;
4249   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
4250   PetscFunctionReturn(PETSC_SUCCESS);
4251 }
4252 
4253 /*@
4254   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
4255 
4256   Not Collective
4257 
4258   Input Parameter:
4259 . dm - The `DMPLEX`
4260 
4261   Output Parameters:
4262 + maxConeSize    - The maximum number of in-edges
4263 - maxSupportSize - The maximum number of out-edges
4264 
4265   Level: beginner
4266 
4267 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
4268 @*/
4269 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
4270 {
4271   DM_Plex *mesh = (DM_Plex *)dm->data;
4272 
4273   PetscFunctionBegin;
4274   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4275   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
4276   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
4277   PetscFunctionReturn(PETSC_SUCCESS);
4278 }
4279 
4280 PetscErrorCode DMSetUp_Plex(DM dm)
4281 {
4282   DM_Plex *mesh = (DM_Plex *)dm->data;
4283   PetscInt size, maxSupportSize;
4284 
4285   PetscFunctionBegin;
4286   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4287   PetscCall(PetscSectionSetUp(mesh->coneSection));
4288   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
4289   PetscCall(PetscMalloc1(size, &mesh->cones));
4290   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
4291   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4292   if (maxSupportSize) {
4293     PetscCall(PetscSectionSetUp(mesh->supportSection));
4294     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
4295     PetscCall(PetscMalloc1(size, &mesh->supports));
4296   }
4297   PetscFunctionReturn(PETSC_SUCCESS);
4298 }
4299 
4300 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
4301 {
4302   PetscFunctionBegin;
4303   if (subdm) PetscCall(DMClone(dm, subdm));
4304   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, NULL, NULL, is, subdm));
4305   if (subdm) (*subdm)->useNatural = dm->useNatural;
4306   if (dm->useNatural && dm->sfMigration) {
4307     PetscSF sfNatural;
4308 
4309     (*subdm)->sfMigration = dm->sfMigration;
4310     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
4311     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
4312     (*subdm)->sfNatural = sfNatural;
4313   }
4314   PetscFunctionReturn(PETSC_SUCCESS);
4315 }
4316 
4317 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
4318 {
4319   PetscInt i = 0;
4320 
4321   PetscFunctionBegin;
4322   PetscCall(DMClone(dms[0], superdm));
4323   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
4324   (*superdm)->useNatural = PETSC_FALSE;
4325   for (i = 0; i < len; i++) {
4326     if (dms[i]->useNatural && dms[i]->sfMigration) {
4327       PetscSF sfNatural;
4328 
4329       (*superdm)->sfMigration = dms[i]->sfMigration;
4330       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
4331       (*superdm)->useNatural = PETSC_TRUE;
4332       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
4333       (*superdm)->sfNatural = sfNatural;
4334       break;
4335     }
4336   }
4337   PetscFunctionReturn(PETSC_SUCCESS);
4338 }
4339 
4340 /*@
4341   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
4342 
4343   Not Collective
4344 
4345   Input Parameter:
4346 . dm - The `DMPLEX`
4347 
4348   Level: beginner
4349 
4350   Note:
4351   This should be called after all calls to `DMPlexSetCone()`
4352 
4353 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4354 @*/
4355 PetscErrorCode DMPlexSymmetrize(DM dm)
4356 {
4357   DM_Plex  *mesh = (DM_Plex *)dm->data;
4358   PetscInt *offsets;
4359   PetscInt  supportSize;
4360   PetscInt  pStart, pEnd, p;
4361 
4362   PetscFunctionBegin;
4363   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4364   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4365   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4366   /* Calculate support sizes */
4367   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4368   for (p = pStart; p < pEnd; ++p) {
4369     PetscInt dof, off, c;
4370 
4371     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4372     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4373     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4374   }
4375   PetscCall(PetscSectionSetUp(mesh->supportSection));
4376   /* Calculate supports */
4377   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4378   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4379   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4380   for (p = pStart; p < pEnd; ++p) {
4381     PetscInt dof, off, c;
4382 
4383     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4384     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4385     for (c = off; c < off + dof; ++c) {
4386       const PetscInt q = mesh->cones[c];
4387       PetscInt       offS;
4388 
4389       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4390 
4391       mesh->supports[offS + offsets[q]] = p;
4392       ++offsets[q];
4393     }
4394   }
4395   PetscCall(PetscFree(offsets));
4396   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4397   PetscFunctionReturn(PETSC_SUCCESS);
4398 }
4399 
4400 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4401 {
4402   IS stratumIS;
4403 
4404   PetscFunctionBegin;
4405   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4406   if (PetscDefined(USE_DEBUG)) {
4407     PetscInt  qStart, qEnd, numLevels, level;
4408     PetscBool overlap = PETSC_FALSE;
4409     PetscCall(DMLabelGetNumValues(label, &numLevels));
4410     for (level = 0; level < numLevels; level++) {
4411       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4412       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4413         overlap = PETSC_TRUE;
4414         break;
4415       }
4416     }
4417     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);
4418   }
4419   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4420   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4421   PetscCall(ISDestroy(&stratumIS));
4422   PetscFunctionReturn(PETSC_SUCCESS);
4423 }
4424 
4425 static PetscErrorCode DMPlexStratify_CellType_Private(DM dm, DMLabel label)
4426 {
4427   PetscInt *pMin, *pMax;
4428   PetscInt  pStart, pEnd;
4429   PetscInt  dmin = PETSC_INT_MAX, dmax = PETSC_INT_MIN;
4430 
4431   PetscFunctionBegin;
4432   {
4433     DMLabel label2;
4434 
4435     PetscCall(DMPlexGetCellTypeLabel(dm, &label2));
4436     PetscCall(PetscObjectViewFromOptions((PetscObject)label2, NULL, "-ct_view"));
4437   }
4438   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4439   for (PetscInt p = pStart; p < pEnd; ++p) {
4440     DMPolytopeType ct;
4441 
4442     PetscCall(DMPlexGetCellType(dm, p, &ct));
4443     dmin = PetscMin(DMPolytopeTypeGetDim(ct), dmin);
4444     dmax = PetscMax(DMPolytopeTypeGetDim(ct), dmax);
4445   }
4446   PetscCall(PetscMalloc2(dmax + 1, &pMin, dmax + 1, &pMax));
4447   for (PetscInt d = dmin; d <= dmax; ++d) {
4448     pMin[d] = PETSC_INT_MAX;
4449     pMax[d] = PETSC_INT_MIN;
4450   }
4451   for (PetscInt p = pStart; p < pEnd; ++p) {
4452     DMPolytopeType ct;
4453     PetscInt       d;
4454 
4455     PetscCall(DMPlexGetCellType(dm, p, &ct));
4456     d       = DMPolytopeTypeGetDim(ct);
4457     pMin[d] = PetscMin(p, pMin[d]);
4458     pMax[d] = PetscMax(p, pMax[d]);
4459   }
4460   for (PetscInt d = dmin; d <= dmax; ++d) {
4461     if (pMin[d] > pMax[d]) continue;
4462     PetscCall(DMPlexCreateDepthStratum(dm, label, d, pMin[d], pMax[d] + 1));
4463   }
4464   PetscCall(PetscFree2(pMin, pMax));
4465   PetscFunctionReturn(PETSC_SUCCESS);
4466 }
4467 
4468 static PetscErrorCode DMPlexStratify_Topological_Private(DM dm, DMLabel label)
4469 {
4470   PetscInt pStart, pEnd;
4471   PetscInt numRoots = 0, numLeaves = 0;
4472 
4473   PetscFunctionBegin;
4474   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4475   {
4476     /* Initialize roots and count leaves */
4477     PetscInt sMin = PETSC_INT_MAX;
4478     PetscInt sMax = PETSC_INT_MIN;
4479     PetscInt coneSize, supportSize;
4480 
4481     for (PetscInt p = pStart; p < pEnd; ++p) {
4482       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4483       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4484       if (!coneSize && supportSize) {
4485         sMin = PetscMin(p, sMin);
4486         sMax = PetscMax(p, sMax);
4487         ++numRoots;
4488       } else if (!supportSize && coneSize) {
4489         ++numLeaves;
4490       } else if (!supportSize && !coneSize) {
4491         /* Isolated points */
4492         sMin = PetscMin(p, sMin);
4493         sMax = PetscMax(p, sMax);
4494       }
4495     }
4496     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4497   }
4498 
4499   if (numRoots + numLeaves == (pEnd - pStart)) {
4500     PetscInt sMin = PETSC_INT_MAX;
4501     PetscInt sMax = PETSC_INT_MIN;
4502     PetscInt coneSize, supportSize;
4503 
4504     for (PetscInt p = pStart; p < pEnd; ++p) {
4505       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4506       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4507       if (!supportSize && coneSize) {
4508         sMin = PetscMin(p, sMin);
4509         sMax = PetscMax(p, sMax);
4510       }
4511     }
4512     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4513   } else {
4514     PetscInt level = 0;
4515     PetscInt qStart, qEnd;
4516 
4517     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4518     while (qEnd > qStart) {
4519       PetscInt sMin = PETSC_INT_MAX;
4520       PetscInt sMax = PETSC_INT_MIN;
4521 
4522       for (PetscInt q = qStart; q < qEnd; ++q) {
4523         const PetscInt *support;
4524         PetscInt        supportSize;
4525 
4526         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4527         PetscCall(DMPlexGetSupport(dm, q, &support));
4528         for (PetscInt s = 0; s < supportSize; ++s) {
4529           sMin = PetscMin(support[s], sMin);
4530           sMax = PetscMax(support[s], sMax);
4531         }
4532       }
4533       PetscCall(DMLabelGetNumValues(label, &level));
4534       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4535       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4536     }
4537   }
4538   PetscFunctionReturn(PETSC_SUCCESS);
4539 }
4540 
4541 /*@
4542   DMPlexStratify - Computes the strata for all points in the `DMPLEX`
4543 
4544   Collective
4545 
4546   Input Parameter:
4547 . dm - The `DMPLEX`
4548 
4549   Level: beginner
4550 
4551   Notes:
4552   The strata group all points of the same grade, and this function calculates the strata. This
4553   grade can be seen as the height (or depth) of the point in the DAG.
4554 
4555   The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4556   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram).
4557   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4558   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4559   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4560   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4561   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4562 
4563   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4564   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4565   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
4566   to interpolate only that one (e0), so that
4567 .vb
4568   cone(c0) = {e0, v2}
4569   cone(e0) = {v0, v1}
4570 .ve
4571   If `DMPlexStratify()` is run on this mesh, it will give depths
4572 .vb
4573    depth 0 = {v0, v1, v2}
4574    depth 1 = {e0, c0}
4575 .ve
4576   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4577 
4578   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4579 
4580 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4581 @*/
4582 PetscErrorCode DMPlexStratify(DM dm)
4583 {
4584   DM_Plex  *mesh = (DM_Plex *)dm->data;
4585   DMLabel   label;
4586   PetscBool flg = PETSC_FALSE;
4587 
4588   PetscFunctionBegin;
4589   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4590   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4591 
4592   // Create depth label
4593   PetscCall(DMRemoveLabel(dm, "depth", NULL));
4594   PetscCall(DMCreateLabel(dm, "depth"));
4595   PetscCall(DMPlexGetDepthLabel(dm, &label));
4596 
4597   PetscCall(PetscOptionsGetBool(NULL, dm->hdr.prefix, "-dm_plex_stratify_celltype", &flg, NULL));
4598   if (flg) PetscCall(DMPlexStratify_CellType_Private(dm, label));
4599   else PetscCall(DMPlexStratify_Topological_Private(dm, label));
4600 
4601   { /* just in case there is an empty process */
4602     PetscInt numValues, maxValues = 0, v;
4603 
4604     PetscCall(DMLabelGetNumValues(label, &numValues));
4605     PetscCallMPI(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4606     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4607   }
4608   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4609   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4610   PetscFunctionReturn(PETSC_SUCCESS);
4611 }
4612 
4613 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4614 {
4615   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4616   PetscInt       dim, depth, pheight, coneSize;
4617 
4618   PetscFunctionBeginHot;
4619   PetscCall(DMGetDimension(dm, &dim));
4620   PetscCall(DMPlexGetDepth(dm, &depth));
4621   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4622   pheight = depth - pdepth;
4623   if (depth <= 1) {
4624     switch (pdepth) {
4625     case 0:
4626       ct = DM_POLYTOPE_POINT;
4627       break;
4628     case 1:
4629       switch (coneSize) {
4630       case 2:
4631         ct = DM_POLYTOPE_SEGMENT;
4632         break;
4633       case 3:
4634         ct = DM_POLYTOPE_TRIANGLE;
4635         break;
4636       case 4:
4637         switch (dim) {
4638         case 2:
4639           ct = DM_POLYTOPE_QUADRILATERAL;
4640           break;
4641         case 3:
4642           ct = DM_POLYTOPE_TETRAHEDRON;
4643           break;
4644         default:
4645           break;
4646         }
4647         break;
4648       case 5:
4649         ct = DM_POLYTOPE_PYRAMID;
4650         break;
4651       case 6:
4652         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4653         break;
4654       case 8:
4655         ct = DM_POLYTOPE_HEXAHEDRON;
4656         break;
4657       default:
4658         break;
4659       }
4660     }
4661   } else {
4662     if (pdepth == 0) {
4663       ct = DM_POLYTOPE_POINT;
4664     } else if (pheight == 0) {
4665       switch (dim) {
4666       case 1:
4667         switch (coneSize) {
4668         case 2:
4669           ct = DM_POLYTOPE_SEGMENT;
4670           break;
4671         default:
4672           break;
4673         }
4674         break;
4675       case 2:
4676         switch (coneSize) {
4677         case 3:
4678           ct = DM_POLYTOPE_TRIANGLE;
4679           break;
4680         case 4:
4681           ct = DM_POLYTOPE_QUADRILATERAL;
4682           break;
4683         default:
4684           break;
4685         }
4686         break;
4687       case 3:
4688         switch (coneSize) {
4689         case 4:
4690           ct = DM_POLYTOPE_TETRAHEDRON;
4691           break;
4692         case 5: {
4693           const PetscInt *cone;
4694           PetscInt        faceConeSize;
4695 
4696           PetscCall(DMPlexGetCone(dm, p, &cone));
4697           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4698           switch (faceConeSize) {
4699           case 3:
4700             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4701             break;
4702           case 4:
4703             ct = DM_POLYTOPE_PYRAMID;
4704             break;
4705           }
4706         } break;
4707         case 6:
4708           ct = DM_POLYTOPE_HEXAHEDRON;
4709           break;
4710         default:
4711           break;
4712         }
4713         break;
4714       default:
4715         break;
4716       }
4717     } else if (pheight > 0) {
4718       switch (coneSize) {
4719       case 2:
4720         ct = DM_POLYTOPE_SEGMENT;
4721         break;
4722       case 3:
4723         ct = DM_POLYTOPE_TRIANGLE;
4724         break;
4725       case 4:
4726         ct = DM_POLYTOPE_QUADRILATERAL;
4727         break;
4728       default:
4729         break;
4730       }
4731     }
4732   }
4733   *pt = ct;
4734   PetscFunctionReturn(PETSC_SUCCESS);
4735 }
4736 
4737 /*@
4738   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4739 
4740   Collective
4741 
4742   Input Parameter:
4743 . dm - The `DMPLEX`
4744 
4745   Level: developer
4746 
4747   Note:
4748   This function is normally called automatically when a cell type is requested. It creates an
4749   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4750   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4751 
4752   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4753 
4754 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4755 @*/
4756 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4757 {
4758   DM_Plex *mesh;
4759   DMLabel  ctLabel;
4760   PetscInt pStart, pEnd, p;
4761 
4762   PetscFunctionBegin;
4763   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4764   mesh = (DM_Plex *)dm->data;
4765   PetscCall(DMCreateLabel(dm, "celltype"));
4766   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4767   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4768   PetscCall(PetscFree(mesh->cellTypes));
4769   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4770   for (p = pStart; p < pEnd; ++p) {
4771     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4772     PetscInt       pdepth;
4773 
4774     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4775     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4776     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]);
4777     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4778     mesh->cellTypes[p - pStart].value_as_uint8 = (uint8_t)ct;
4779   }
4780   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4781   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4782   PetscFunctionReturn(PETSC_SUCCESS);
4783 }
4784 
4785 /*@C
4786   DMPlexGetJoin - Get an array for the join of the set of points
4787 
4788   Not Collective
4789 
4790   Input Parameters:
4791 + dm        - The `DMPLEX` object
4792 . numPoints - The number of input points for the join
4793 - points    - The input points
4794 
4795   Output Parameters:
4796 + numCoveredPoints - The number of points in the join
4797 - coveredPoints    - The points in the join
4798 
4799   Level: intermediate
4800 
4801   Note:
4802   Currently, this is restricted to a single level join
4803 
4804   Fortran Notes:
4805   `converedPoints` must be declared with
4806 .vb
4807   PetscInt, pointer :: coveredPints(:)
4808 .ve
4809 
4810   The `numCoveredPoints` argument is not present in the Fortran binding.
4811 
4812 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4813 @*/
4814 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4815 {
4816   DM_Plex  *mesh = (DM_Plex *)dm->data;
4817   PetscInt *join[2];
4818   PetscInt  joinSize, i = 0;
4819   PetscInt  dof, off, p, c, m;
4820   PetscInt  maxSupportSize;
4821 
4822   PetscFunctionBegin;
4823   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4824   PetscAssertPointer(points, 3);
4825   PetscAssertPointer(numCoveredPoints, 4);
4826   PetscAssertPointer(coveredPoints, 5);
4827   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4828   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4829   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4830   /* Copy in support of first point */
4831   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4832   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4833   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4834   /* Check each successive support */
4835   for (p = 1; p < numPoints; ++p) {
4836     PetscInt newJoinSize = 0;
4837 
4838     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4839     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4840     for (c = 0; c < dof; ++c) {
4841       const PetscInt point = mesh->supports[off + c];
4842 
4843       for (m = 0; m < joinSize; ++m) {
4844         if (point == join[i][m]) {
4845           join[1 - i][newJoinSize++] = point;
4846           break;
4847         }
4848       }
4849     }
4850     joinSize = newJoinSize;
4851     i        = 1 - i;
4852   }
4853   *numCoveredPoints = joinSize;
4854   *coveredPoints    = join[i];
4855   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4856   PetscFunctionReturn(PETSC_SUCCESS);
4857 }
4858 
4859 /*@C
4860   DMPlexRestoreJoin - Restore an array for the join of the set of points obtained with `DMPlexGetJoin()`
4861 
4862   Not Collective
4863 
4864   Input Parameters:
4865 + dm        - The `DMPLEX` object
4866 . numPoints - The number of input points for the join
4867 - points    - The input points
4868 
4869   Output Parameters:
4870 + numCoveredPoints - The number of points in the join
4871 - coveredPoints    - The points in the join
4872 
4873   Level: intermediate
4874 
4875   Fortran Notes:
4876   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4877 
4878 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4879 @*/
4880 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4881 {
4882   PetscFunctionBegin;
4883   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4884   if (points) PetscAssertPointer(points, 3);
4885   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4886   PetscAssertPointer(coveredPoints, 5);
4887   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4888   if (numCoveredPoints) *numCoveredPoints = 0;
4889   PetscFunctionReturn(PETSC_SUCCESS);
4890 }
4891 
4892 /*@C
4893   DMPlexGetFullJoin - Get an array for the join of the set of points
4894 
4895   Not Collective
4896 
4897   Input Parameters:
4898 + dm        - The `DMPLEX` object
4899 . numPoints - The number of input points for the join
4900 - points    - The input points, its length is `numPoints`
4901 
4902   Output Parameters:
4903 + numCoveredPoints - The number of points in the join
4904 - coveredPoints    - The points in the join, its length is `numCoveredPoints`
4905 
4906   Level: intermediate
4907 
4908   Fortran Notes:
4909   `points` and `converedPoints` must be declared with
4910 .vb
4911   PetscInt, pointer :: points(:)
4912   PetscInt, pointer :: coveredPints(:)
4913 .ve
4914 
4915   The `numCoveredPoints` argument is not present in the Fortran binding.
4916 
4917 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4918 @*/
4919 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4920 {
4921   PetscInt *offsets, **closures;
4922   PetscInt *join[2];
4923   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4924   PetscInt  p, d, c, m, ms;
4925 
4926   PetscFunctionBegin;
4927   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4928   PetscAssertPointer(points, 3);
4929   PetscAssertPointer(numCoveredPoints, 4);
4930   PetscAssertPointer(coveredPoints, 5);
4931 
4932   PetscCall(DMPlexGetDepth(dm, &depth));
4933   PetscCall(PetscCalloc1(numPoints, &closures));
4934   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4935   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4936   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4937   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4938   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4939 
4940   for (p = 0; p < numPoints; ++p) {
4941     PetscInt closureSize;
4942 
4943     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4944 
4945     offsets[p * (depth + 2) + 0] = 0;
4946     for (d = 0; d < depth + 1; ++d) {
4947       PetscInt pStart, pEnd, i;
4948 
4949       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4950       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4951         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4952           offsets[p * (depth + 2) + d + 1] = i;
4953           break;
4954         }
4955       }
4956       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4957     }
4958     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);
4959   }
4960   for (d = 0; d < depth + 1; ++d) {
4961     PetscInt dof;
4962 
4963     /* Copy in support of first point */
4964     dof = offsets[d + 1] - offsets[d];
4965     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4966     /* Check each successive cone */
4967     for (p = 1; p < numPoints && joinSize; ++p) {
4968       PetscInt newJoinSize = 0;
4969 
4970       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4971       for (c = 0; c < dof; ++c) {
4972         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4973 
4974         for (m = 0; m < joinSize; ++m) {
4975           if (point == join[i][m]) {
4976             join[1 - i][newJoinSize++] = point;
4977             break;
4978           }
4979         }
4980       }
4981       joinSize = newJoinSize;
4982       i        = 1 - i;
4983     }
4984     if (joinSize) break;
4985   }
4986   *numCoveredPoints = joinSize;
4987   *coveredPoints    = join[i];
4988   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4989   PetscCall(PetscFree(closures));
4990   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4991   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4992   PetscFunctionReturn(PETSC_SUCCESS);
4993 }
4994 
4995 /*@C
4996   DMPlexGetMeet - Get an array for the meet of the set of points
4997 
4998   Not Collective
4999 
5000   Input Parameters:
5001 + dm        - The `DMPLEX` object
5002 . numPoints - The number of input points for the meet
5003 - points    - The input points, of length `numPoints`
5004 
5005   Output Parameters:
5006 + numCoveringPoints - The number of points in the meet
5007 - coveringPoints    - The points in the meet, of length `numCoveringPoints`
5008 
5009   Level: intermediate
5010 
5011   Note:
5012   Currently, this is restricted to a single level meet
5013 
5014   Fortran Notes:
5015   `coveringPoints` must be declared with
5016 .vb
5017   PetscInt, pointer :: coveringPoints(:)
5018 .ve
5019 
5020   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
5021 
5022 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
5023 @*/
5024 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt *coveringPoints[])
5025 {
5026   DM_Plex  *mesh = (DM_Plex *)dm->data;
5027   PetscInt *meet[2];
5028   PetscInt  meetSize, i = 0;
5029   PetscInt  dof, off, p, c, m;
5030   PetscInt  maxConeSize;
5031 
5032   PetscFunctionBegin;
5033   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5034   PetscAssertPointer(points, 3);
5035   PetscAssertPointer(numCoveringPoints, 4);
5036   PetscAssertPointer(coveringPoints, 5);
5037   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
5038   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
5039   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
5040   /* Copy in cone of first point */
5041   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
5042   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
5043   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
5044   /* Check each successive cone */
5045   for (p = 1; p < numPoints; ++p) {
5046     PetscInt newMeetSize = 0;
5047 
5048     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
5049     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
5050     for (c = 0; c < dof; ++c) {
5051       const PetscInt point = mesh->cones[off + c];
5052 
5053       for (m = 0; m < meetSize; ++m) {
5054         if (point == meet[i][m]) {
5055           meet[1 - i][newMeetSize++] = point;
5056           break;
5057         }
5058       }
5059     }
5060     meetSize = newMeetSize;
5061     i        = 1 - i;
5062   }
5063   *numCoveringPoints = meetSize;
5064   *coveringPoints    = meet[i];
5065   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
5066   PetscFunctionReturn(PETSC_SUCCESS);
5067 }
5068 
5069 /*@C
5070   DMPlexRestoreMeet - Restore an array for the meet of the set of points obtained with `DMPlexGetMeet()`
5071 
5072   Not Collective
5073 
5074   Input Parameters:
5075 + dm        - The `DMPLEX` object
5076 . numPoints - The number of input points for the meet
5077 - points    - The input points
5078 
5079   Output Parameters:
5080 + numCoveredPoints - The number of points in the meet
5081 - coveredPoints    - The points in the meet
5082 
5083   Level: intermediate
5084 
5085   Fortran Notes:
5086   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
5087 
5088 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
5089 @*/
5090 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
5091 {
5092   PetscFunctionBegin;
5093   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5094   if (points) PetscAssertPointer(points, 3);
5095   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
5096   PetscAssertPointer(coveredPoints, 5);
5097   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
5098   if (numCoveredPoints) *numCoveredPoints = 0;
5099   PetscFunctionReturn(PETSC_SUCCESS);
5100 }
5101 
5102 /*@C
5103   DMPlexGetFullMeet - Get an array for the meet of the set of points
5104 
5105   Not Collective
5106 
5107   Input Parameters:
5108 + dm        - The `DMPLEX` object
5109 . numPoints - The number of input points for the meet
5110 - points    - The input points, of length  `numPoints`
5111 
5112   Output Parameters:
5113 + numCoveredPoints - The number of points in the meet
5114 - coveredPoints    - The points in the meet, of length  `numCoveredPoints`
5115 
5116   Level: intermediate
5117 
5118   Fortran Notes:
5119   `points` and `coveredPoints` must be declared with
5120 .vb
5121   PetscInt, pointer :: points(:)
5122   PetscInt, pointer :: coveredPoints(:)
5123 .ve
5124 
5125   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
5126 
5127 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
5128 @*/
5129 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
5130 {
5131   PetscInt *offsets, **closures;
5132   PetscInt *meet[2];
5133   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
5134   PetscInt  p, h, c, m, mc;
5135 
5136   PetscFunctionBegin;
5137   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5138   PetscAssertPointer(points, 3);
5139   PetscAssertPointer(numCoveredPoints, 4);
5140   PetscAssertPointer(coveredPoints, 5);
5141 
5142   PetscCall(DMPlexGetDepth(dm, &height));
5143   PetscCall(PetscMalloc1(numPoints, &closures));
5144   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5145   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
5146   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
5147   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
5148   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
5149 
5150   for (p = 0; p < numPoints; ++p) {
5151     PetscInt closureSize;
5152 
5153     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
5154 
5155     offsets[p * (height + 2) + 0] = 0;
5156     for (h = 0; h < height + 1; ++h) {
5157       PetscInt pStart, pEnd, i;
5158 
5159       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
5160       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
5161         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
5162           offsets[p * (height + 2) + h + 1] = i;
5163           break;
5164         }
5165       }
5166       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
5167     }
5168     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);
5169   }
5170   for (h = 0; h < height + 1; ++h) {
5171     PetscInt dof;
5172 
5173     /* Copy in cone of first point */
5174     dof = offsets[h + 1] - offsets[h];
5175     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
5176     /* Check each successive cone */
5177     for (p = 1; p < numPoints && meetSize; ++p) {
5178       PetscInt newMeetSize = 0;
5179 
5180       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
5181       for (c = 0; c < dof; ++c) {
5182         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
5183 
5184         for (m = 0; m < meetSize; ++m) {
5185           if (point == meet[i][m]) {
5186             meet[1 - i][newMeetSize++] = point;
5187             break;
5188           }
5189         }
5190       }
5191       meetSize = newMeetSize;
5192       i        = 1 - i;
5193     }
5194     if (meetSize) break;
5195   }
5196   *numCoveredPoints = meetSize;
5197   *coveredPoints    = meet[i];
5198   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
5199   PetscCall(PetscFree(closures));
5200   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5201   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
5202   PetscFunctionReturn(PETSC_SUCCESS);
5203 }
5204 
5205 /*@
5206   DMPlexEqual - Determine if two `DM` have the same topology
5207 
5208   Not Collective
5209 
5210   Input Parameters:
5211 + dmA - A `DMPLEX` object
5212 - dmB - A `DMPLEX` object
5213 
5214   Output Parameter:
5215 . equal - `PETSC_TRUE` if the topologies are identical
5216 
5217   Level: intermediate
5218 
5219   Note:
5220   We are not solving graph isomorphism, so we do not permute.
5221 
5222 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5223 @*/
5224 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
5225 {
5226   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
5227 
5228   PetscFunctionBegin;
5229   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
5230   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
5231   PetscAssertPointer(equal, 3);
5232 
5233   *equal = PETSC_FALSE;
5234   PetscCall(DMPlexGetDepth(dmA, &depth));
5235   PetscCall(DMPlexGetDepth(dmB, &depthB));
5236   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
5237   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
5238   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
5239   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
5240   for (p = pStart; p < pEnd; ++p) {
5241     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
5242     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
5243 
5244     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
5245     PetscCall(DMPlexGetCone(dmA, p, &cone));
5246     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
5247     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
5248     PetscCall(DMPlexGetCone(dmB, p, &coneB));
5249     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
5250     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5251     for (c = 0; c < coneSize; ++c) {
5252       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5253       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5254     }
5255     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
5256     PetscCall(DMPlexGetSupport(dmA, p, &support));
5257     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
5258     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
5259     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5260     for (s = 0; s < supportSize; ++s) {
5261       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
5262     }
5263   }
5264   *equal = PETSC_TRUE;
5265   PetscFunctionReturn(PETSC_SUCCESS);
5266 }
5267 
5268 /*@
5269   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
5270 
5271   Not Collective
5272 
5273   Input Parameters:
5274 + dm         - The `DMPLEX`
5275 . cellDim    - The cell dimension
5276 - numCorners - The number of vertices on a cell
5277 
5278   Output Parameter:
5279 . numFaceVertices - The number of vertices on a face
5280 
5281   Level: developer
5282 
5283   Note:
5284   Of course this can only work for a restricted set of symmetric shapes
5285 
5286 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5287 @*/
5288 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
5289 {
5290   MPI_Comm comm;
5291 
5292   PetscFunctionBegin;
5293   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
5294   PetscAssertPointer(numFaceVertices, 4);
5295   switch (cellDim) {
5296   case 0:
5297     *numFaceVertices = 0;
5298     break;
5299   case 1:
5300     *numFaceVertices = 1;
5301     break;
5302   case 2:
5303     switch (numCorners) {
5304     case 3:                 /* triangle */
5305       *numFaceVertices = 2; /* Edge has 2 vertices */
5306       break;
5307     case 4:                 /* quadrilateral */
5308       *numFaceVertices = 2; /* Edge has 2 vertices */
5309       break;
5310     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
5311       *numFaceVertices = 3; /* Edge has 3 vertices */
5312       break;
5313     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
5314       *numFaceVertices = 3; /* Edge has 3 vertices */
5315       break;
5316     default:
5317       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5318     }
5319     break;
5320   case 3:
5321     switch (numCorners) {
5322     case 4:                 /* tetradehdron */
5323       *numFaceVertices = 3; /* Face has 3 vertices */
5324       break;
5325     case 6:                 /* tet cohesive cells */
5326       *numFaceVertices = 4; /* Face has 4 vertices */
5327       break;
5328     case 8:                 /* hexahedron */
5329       *numFaceVertices = 4; /* Face has 4 vertices */
5330       break;
5331     case 9:                 /* tet cohesive Lagrange cells */
5332       *numFaceVertices = 6; /* Face has 6 vertices */
5333       break;
5334     case 10:                /* quadratic tetrahedron */
5335       *numFaceVertices = 6; /* Face has 6 vertices */
5336       break;
5337     case 12:                /* hex cohesive Lagrange cells */
5338       *numFaceVertices = 6; /* Face has 6 vertices */
5339       break;
5340     case 18:                /* quadratic tet cohesive Lagrange cells */
5341       *numFaceVertices = 6; /* Face has 6 vertices */
5342       break;
5343     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
5344       *numFaceVertices = 9; /* Face has 9 vertices */
5345       break;
5346     default:
5347       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5348     }
5349     break;
5350   default:
5351     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
5352   }
5353   PetscFunctionReturn(PETSC_SUCCESS);
5354 }
5355 
5356 /*@
5357   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
5358 
5359   Not Collective
5360 
5361   Input Parameter:
5362 . dm - The `DMPLEX` object
5363 
5364   Output Parameter:
5365 . depthLabel - The `DMLabel` recording point depth
5366 
5367   Level: developer
5368 
5369 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
5370 @*/
5371 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
5372 {
5373   PetscFunctionBegin;
5374   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5375   PetscAssertPointer(depthLabel, 2);
5376   *depthLabel = dm->depthLabel;
5377   PetscFunctionReturn(PETSC_SUCCESS);
5378 }
5379 
5380 /*@
5381   DMPlexGetDepth - Get the depth of the DAG representing this mesh
5382 
5383   Not Collective
5384 
5385   Input Parameter:
5386 . dm - The `DMPLEX` object
5387 
5388   Output Parameter:
5389 . depth - The number of strata (breadth first levels) in the DAG
5390 
5391   Level: developer
5392 
5393   Notes:
5394   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
5395 
5396   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
5397 
5398   An empty mesh gives -1.
5399 
5400 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
5401 @*/
5402 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
5403 {
5404   DM_Plex *mesh = (DM_Plex *)dm->data;
5405   DMLabel  label;
5406   PetscInt d = -1;
5407 
5408   PetscFunctionBegin;
5409   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5410   PetscAssertPointer(depth, 2);
5411   if (mesh->tr) {
5412     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
5413   } else {
5414     PetscCall(DMPlexGetDepthLabel(dm, &label));
5415     // Allow missing depths
5416     if (label) PetscCall(DMLabelGetValueBounds(label, NULL, &d));
5417     *depth = d;
5418   }
5419   PetscFunctionReturn(PETSC_SUCCESS);
5420 }
5421 
5422 /*@
5423   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
5424 
5425   Not Collective
5426 
5427   Input Parameters:
5428 + dm    - The `DMPLEX` object
5429 - depth - The requested depth
5430 
5431   Output Parameters:
5432 + start - The first point at this `depth`
5433 - end   - One beyond the last point at this `depth`
5434 
5435   Level: developer
5436 
5437   Notes:
5438   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5439   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5440   higher dimension, e.g., "edges".
5441 
5442 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5443 @*/
5444 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
5445 {
5446   DM_Plex *mesh = (DM_Plex *)dm->data;
5447   DMLabel  label;
5448   PetscInt pStart, pEnd;
5449 
5450   PetscFunctionBegin;
5451   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5452   if (start) {
5453     PetscAssertPointer(start, 3);
5454     *start = 0;
5455   }
5456   if (end) {
5457     PetscAssertPointer(end, 4);
5458     *end = 0;
5459   }
5460   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5461   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5462   if (depth < 0) {
5463     if (start) *start = pStart;
5464     if (end) *end = pEnd;
5465     PetscFunctionReturn(PETSC_SUCCESS);
5466   }
5467   if (mesh->tr) {
5468     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5469   } else {
5470     PetscCall(DMPlexGetDepthLabel(dm, &label));
5471     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5472     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5473   }
5474   PetscFunctionReturn(PETSC_SUCCESS);
5475 }
5476 
5477 /*@
5478   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5479 
5480   Not Collective
5481 
5482   Input Parameters:
5483 + dm     - The `DMPLEX` object
5484 - height - The requested height
5485 
5486   Output Parameters:
5487 + start - The first point at this `height`
5488 - end   - One beyond the last point at this `height`
5489 
5490   Level: developer
5491 
5492   Notes:
5493   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5494   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5495   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5496 
5497 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5498 @*/
5499 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5500 {
5501   DMLabel  label;
5502   PetscInt depth, pStart, pEnd;
5503 
5504   PetscFunctionBegin;
5505   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5506   if (start) {
5507     PetscAssertPointer(start, 3);
5508     *start = 0;
5509   }
5510   if (end) {
5511     PetscAssertPointer(end, 4);
5512     *end = 0;
5513   }
5514   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5515   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5516   if (height < 0) {
5517     if (start) *start = pStart;
5518     if (end) *end = pEnd;
5519     PetscFunctionReturn(PETSC_SUCCESS);
5520   }
5521   PetscCall(DMPlexGetDepthLabel(dm, &label));
5522   if (label) PetscCall(DMLabelGetNumValues(label, &depth));
5523   else PetscCall(DMGetDimension(dm, &depth));
5524   PetscCheck(depth >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Depth not yet computed");
5525   PetscCall(DMPlexGetDepthStratum(dm, depth - 1 - height, start, end));
5526   PetscFunctionReturn(PETSC_SUCCESS);
5527 }
5528 
5529 /*@
5530   DMPlexGetPointDepth - Get the `depth` of a given point
5531 
5532   Not Collective
5533 
5534   Input Parameters:
5535 + dm    - The `DMPLEX` object
5536 - point - The point
5537 
5538   Output Parameter:
5539 . depth - The depth of the `point`
5540 
5541   Level: intermediate
5542 
5543 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5544 @*/
5545 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5546 {
5547   PetscFunctionBegin;
5548   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5549   PetscAssertPointer(depth, 3);
5550   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5551   PetscFunctionReturn(PETSC_SUCCESS);
5552 }
5553 
5554 /*@
5555   DMPlexGetPointHeight - Get the `height` of a given point
5556 
5557   Not Collective
5558 
5559   Input Parameters:
5560 + dm    - The `DMPLEX` object
5561 - point - The point
5562 
5563   Output Parameter:
5564 . height - The height of the `point`
5565 
5566   Level: intermediate
5567 
5568 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5569 @*/
5570 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5571 {
5572   PetscInt n, pDepth;
5573 
5574   PetscFunctionBegin;
5575   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5576   PetscAssertPointer(height, 3);
5577   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5578   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5579   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5580   PetscFunctionReturn(PETSC_SUCCESS);
5581 }
5582 
5583 /*@
5584   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5585 
5586   Not Collective
5587 
5588   Input Parameter:
5589 . dm - The `DMPLEX` object
5590 
5591   Output Parameter:
5592 . celltypeLabel - The `DMLabel` recording cell polytope type
5593 
5594   Level: developer
5595 
5596   Note:
5597   This function will trigger automatica computation of cell types. This can be disabled by calling
5598   `DMCreateLabel`(dm, "celltype") beforehand.
5599 
5600 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5601 @*/
5602 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5603 {
5604   PetscFunctionBegin;
5605   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5606   PetscAssertPointer(celltypeLabel, 2);
5607   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5608   *celltypeLabel = dm->celltypeLabel;
5609   PetscFunctionReturn(PETSC_SUCCESS);
5610 }
5611 
5612 /*@
5613   DMPlexGetCellType - Get the polytope type of a given cell
5614 
5615   Not Collective
5616 
5617   Input Parameters:
5618 + dm   - The `DMPLEX` object
5619 - cell - The cell
5620 
5621   Output Parameter:
5622 . celltype - The polytope type of the cell
5623 
5624   Level: intermediate
5625 
5626 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5627 @*/
5628 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5629 {
5630   DM_Plex *mesh = (DM_Plex *)dm->data;
5631   DMLabel  label;
5632   PetscInt ct;
5633 
5634   PetscFunctionBegin;
5635   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5636   PetscAssertPointer(celltype, 3);
5637   if (mesh->tr) {
5638     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5639   } else {
5640     PetscInt pStart, pEnd;
5641 
5642     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5643     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5644       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5645       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5646       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5647       for (PetscInt p = pStart; p < pEnd; p++) {
5648         PetscCall(DMLabelGetValue(label, p, &ct));
5649         mesh->cellTypes[p - pStart].value_as_uint8 = (uint8_t)ct;
5650       }
5651     }
5652     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5653     if (PetscDefined(USE_DEBUG)) {
5654       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5655       PetscCall(DMLabelGetValue(label, cell, &ct));
5656       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5657       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5658     }
5659   }
5660   PetscFunctionReturn(PETSC_SUCCESS);
5661 }
5662 
5663 /*@
5664   DMPlexSetCellType - Set the polytope type of a given cell
5665 
5666   Not Collective
5667 
5668   Input Parameters:
5669 + dm       - The `DMPLEX` object
5670 . cell     - The cell
5671 - celltype - The polytope type of the cell
5672 
5673   Level: advanced
5674 
5675   Note:
5676   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5677   is executed. This function will override the computed type. However, if automatic classification will not succeed
5678   and a user wants to manually specify all types, the classification must be disabled by calling
5679   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5680 
5681 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5682 @*/
5683 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5684 {
5685   DM_Plex *mesh = (DM_Plex *)dm->data;
5686   DMLabel  label;
5687   PetscInt pStart, pEnd;
5688 
5689   PetscFunctionBegin;
5690   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5691   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5692   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5693   PetscCall(DMLabelSetValue(label, cell, celltype));
5694   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5695   mesh->cellTypes[cell - pStart].value_as_uint8 = (uint8_t)celltype;
5696   PetscFunctionReturn(PETSC_SUCCESS);
5697 }
5698 
5699 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5700 {
5701   PetscSection section;
5702   PetscInt     maxHeight;
5703   const char  *prefix;
5704 
5705   PetscFunctionBegin;
5706   PetscCall(DMClone(dm, cdm));
5707   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5708   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5709   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5710   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5711   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5712   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5713   PetscCall(DMSetLocalSection(*cdm, section));
5714   PetscCall(PetscSectionDestroy(&section));
5715 
5716   PetscCall(DMSetNumFields(*cdm, 1));
5717   PetscCall(DMCreateDS(*cdm));
5718   (*cdm)->cloneOpts = PETSC_TRUE;
5719   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5720   PetscFunctionReturn(PETSC_SUCCESS);
5721 }
5722 
5723 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5724 {
5725   Vec coordsLocal, cellCoordsLocal;
5726   DM  coordsDM, cellCoordsDM;
5727 
5728   PetscFunctionBegin;
5729   *field = NULL;
5730   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5731   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5732   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5733   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5734   if (coordsLocal && coordsDM) {
5735     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5736     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5737   }
5738   PetscFunctionReturn(PETSC_SUCCESS);
5739 }
5740 
5741 /*@
5742   DMPlexGetConeSection - Return a section which describes the layout of cone data
5743 
5744   Not Collective
5745 
5746   Input Parameter:
5747 . dm - The `DMPLEX` object
5748 
5749   Output Parameter:
5750 . section - The `PetscSection` object
5751 
5752   Level: developer
5753 
5754 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5755 @*/
5756 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5757 {
5758   DM_Plex *mesh = (DM_Plex *)dm->data;
5759 
5760   PetscFunctionBegin;
5761   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5762   if (section) *section = mesh->coneSection;
5763   PetscFunctionReturn(PETSC_SUCCESS);
5764 }
5765 
5766 /*@
5767   DMPlexGetSupportSection - Return a section which describes the layout of support data
5768 
5769   Not Collective
5770 
5771   Input Parameter:
5772 . dm - The `DMPLEX` object
5773 
5774   Output Parameter:
5775 . section - The `PetscSection` object
5776 
5777   Level: developer
5778 
5779 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5780 @*/
5781 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5782 {
5783   DM_Plex *mesh = (DM_Plex *)dm->data;
5784 
5785   PetscFunctionBegin;
5786   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5787   if (section) *section = mesh->supportSection;
5788   PetscFunctionReturn(PETSC_SUCCESS);
5789 }
5790 
5791 /*@C
5792   DMPlexGetCones - Return cone data
5793 
5794   Not Collective
5795 
5796   Input Parameter:
5797 . dm - The `DMPLEX` object
5798 
5799   Output Parameter:
5800 . cones - The cone for each point
5801 
5802   Level: developer
5803 
5804 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5805 @*/
5806 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5807 {
5808   DM_Plex *mesh = (DM_Plex *)dm->data;
5809 
5810   PetscFunctionBegin;
5811   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5812   if (cones) *cones = mesh->cones;
5813   PetscFunctionReturn(PETSC_SUCCESS);
5814 }
5815 
5816 /*@C
5817   DMPlexGetConeOrientations - Return cone orientation data
5818 
5819   Not Collective
5820 
5821   Input Parameter:
5822 . dm - The `DMPLEX` object
5823 
5824   Output Parameter:
5825 . coneOrientations - The array of cone orientations for all points
5826 
5827   Level: developer
5828 
5829   Notes:
5830   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points
5831   as returned by `DMPlexGetConeOrientation()`.
5832 
5833   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5834 
5835 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5836 @*/
5837 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5838 {
5839   DM_Plex *mesh = (DM_Plex *)dm->data;
5840 
5841   PetscFunctionBegin;
5842   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5843   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5844   PetscFunctionReturn(PETSC_SUCCESS);
5845 }
5846 
5847 /* FEM Support */
5848 
5849 PetscErrorCode DMPlexGetAllCells_Internal(DM plex, IS *cellIS)
5850 {
5851   PetscInt depth;
5852 
5853   PetscFunctionBegin;
5854   PetscCall(DMPlexGetDepth(plex, &depth));
5855   PetscCall(DMGetStratumIS(plex, "dim", depth, cellIS));
5856   if (!*cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, cellIS));
5857   PetscFunctionReturn(PETSC_SUCCESS);
5858 }
5859 
5860 PetscErrorCode DMPlexGetAllFaces_Internal(DM plex, IS *faceIS)
5861 {
5862   PetscInt depth;
5863 
5864   PetscFunctionBegin;
5865   PetscCall(DMPlexGetDepth(plex, &depth));
5866   PetscCall(DMGetStratumIS(plex, "dim", depth - 1, faceIS));
5867   if (!*faceIS) PetscCall(DMGetStratumIS(plex, "depth", depth - 1, faceIS));
5868   PetscFunctionReturn(PETSC_SUCCESS);
5869 }
5870 
5871 /*
5872  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5873  representing a line in the section.
5874 */
5875 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(DM dm, PetscSection section, PetscInt field, PetscInt line, PetscInt *Nc, PetscInt *k, PetscBool *continuous, PetscBool *tensor)
5876 {
5877   PetscObject  obj;
5878   PetscClassId id;
5879   PetscFE      fe = NULL;
5880 
5881   PetscFunctionBeginHot;
5882   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5883   PetscCall(DMGetField(dm, field, NULL, &obj));
5884   PetscCall(PetscObjectGetClassId(obj, &id));
5885   if (id == PETSCFE_CLASSID) fe = (PetscFE)obj;
5886 
5887   if (!fe) {
5888     /* Assume the full interpolated mesh is in the chart; lines in particular */
5889     /* An order k SEM disc has k-1 dofs on an edge */
5890     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5891     *k = *k / *Nc + 1;
5892   } else {
5893     PetscInt       dual_space_size, dim;
5894     PetscDualSpace dsp;
5895 
5896     PetscCall(DMGetDimension(dm, &dim));
5897     PetscCall(PetscFEGetDualSpace(fe, &dsp));
5898     PetscCall(PetscDualSpaceGetDimension(dsp, &dual_space_size));
5899     *k = (PetscInt)PetscCeilReal(PetscPowReal(dual_space_size / *Nc, 1.0 / dim)) - 1;
5900     PetscCall(PetscDualSpaceLagrangeGetContinuity(dsp, continuous));
5901     PetscCall(PetscDualSpaceLagrangeGetTensor(dsp, tensor));
5902   }
5903   PetscFunctionReturn(PETSC_SUCCESS);
5904 }
5905 
5906 static PetscErrorCode GetFieldSize_Private(PetscInt dim, PetscInt k, PetscBool tensor, PetscInt *dof)
5907 {
5908   PetscFunctionBeginHot;
5909   if (tensor) {
5910     *dof = PetscPowInt(k + 1, dim);
5911   } else {
5912     switch (dim) {
5913     case 1:
5914       *dof = k + 1;
5915       break;
5916     case 2:
5917       *dof = ((k + 1) * (k + 2)) / 2;
5918       break;
5919     case 3:
5920       *dof = ((k + 1) * (k + 2) * (k + 3)) / 6;
5921       break;
5922     default:
5923       *dof = 0;
5924     }
5925   }
5926   PetscFunctionReturn(PETSC_SUCCESS);
5927 }
5928 
5929 /*@
5930   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5931   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5932   section provided (or the section of the `DM`).
5933 
5934   Input Parameters:
5935 + dm      - The `DM`
5936 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5937 - section - The `PetscSection` to reorder, or `NULL` for the default section
5938 
5939   Example:
5940   A typical interpolated single-quad mesh might order points as
5941 .vb
5942   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5943 
5944   v4 -- e6 -- v3
5945   |           |
5946   e7    c0    e8
5947   |           |
5948   v1 -- e5 -- v2
5949 .ve
5950 
5951   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5952   dofs in the order of points, e.g.,
5953 .vb
5954     c0 -> [0,1,2,3]
5955     v1 -> [4]
5956     ...
5957     e5 -> [8, 9]
5958 .ve
5959 
5960   which corresponds to the dofs
5961 .vb
5962     6   10  11  7
5963     13  2   3   15
5964     12  0   1   14
5965     4   8   9   5
5966 .ve
5967 
5968   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5969 .vb
5970   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5971 .ve
5972 
5973   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5974 .vb
5975    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5976 .ve
5977 
5978   Level: developer
5979 
5980   Notes:
5981   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5982   degree of the basis.
5983 
5984   This is required to run with libCEED.
5985 
5986 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5987 @*/
5988 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5989 {
5990   DMLabel   label;
5991   PetscInt  dim, depth = -1, eStart = -1, Nf;
5992   PetscBool continuous = PETSC_TRUE, tensor = PETSC_TRUE;
5993 
5994   PetscFunctionBegin;
5995   PetscCall(DMGetDimension(dm, &dim));
5996   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5997   if (point < 0) {
5998     PetscInt sStart, sEnd;
5999 
6000     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
6001     point = sEnd - sStart ? sStart : point;
6002   }
6003   PetscCall(DMPlexGetDepthLabel(dm, &label));
6004   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
6005   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6006   if (depth == 1) {
6007     eStart = point;
6008   } else if (depth == dim) {
6009     const PetscInt *cone;
6010 
6011     PetscCall(DMPlexGetCone(dm, point, &cone));
6012     if (dim == 2) eStart = cone[0];
6013     else if (dim == 3) {
6014       const PetscInt *cone2;
6015       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
6016       eStart = cone2[0];
6017     } 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);
6018   } 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);
6019 
6020   PetscCall(PetscSectionGetNumFields(section, &Nf));
6021   for (PetscInt d = 1; d <= dim; d++) {
6022     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
6023     PetscInt *perm;
6024 
6025     for (f = 0; f < Nf; ++f) {
6026       PetscInt dof;
6027 
6028       PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6029       PetscCheck(dim == 1 || tensor || !continuous, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Continuous field %" PetscInt_FMT " must have a tensor product discretization", f);
6030       if (!continuous && d < dim) continue;
6031       PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6032       size += dof * Nc;
6033     }
6034     PetscCall(PetscMalloc1(size, &perm));
6035     for (f = 0; f < Nf; ++f) {
6036       switch (d) {
6037       case 1:
6038         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6039         if (!continuous && d < dim) continue;
6040         /*
6041          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
6042          We want              [ vtx0; edge of length k-1; vtx1 ]
6043          */
6044         if (continuous) {
6045           for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
6046           for (i = 0; i < k - 1; i++)
6047             for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
6048           for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
6049           foffset = offset;
6050         } else {
6051           PetscInt dof;
6052 
6053           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6054           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6055           foffset = offset;
6056         }
6057         break;
6058       case 2:
6059         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
6060         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6061         if (!continuous && d < dim) continue;
6062         /* The SEM order is
6063 
6064          v_lb, {e_b}, v_rb,
6065          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
6066          v_lt, reverse {e_t}, v_rt
6067          */
6068         if (continuous) {
6069           const PetscInt of   = 0;
6070           const PetscInt oeb  = of + PetscSqr(k - 1);
6071           const PetscInt oer  = oeb + (k - 1);
6072           const PetscInt oet  = oer + (k - 1);
6073           const PetscInt oel  = oet + (k - 1);
6074           const PetscInt ovlb = oel + (k - 1);
6075           const PetscInt ovrb = ovlb + 1;
6076           const PetscInt ovrt = ovrb + 1;
6077           const PetscInt ovlt = ovrt + 1;
6078           PetscInt       o;
6079 
6080           /* bottom */
6081           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
6082           for (o = oeb; o < oer; ++o)
6083             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6084           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
6085           /* middle */
6086           for (i = 0; i < k - 1; ++i) {
6087             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
6088             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
6089               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6090             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
6091           }
6092           /* top */
6093           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
6094           for (o = oel - 1; o >= oet; --o)
6095             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6096           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
6097           foffset = offset;
6098         } else {
6099           PetscInt dof;
6100 
6101           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6102           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6103           foffset = offset;
6104         }
6105         break;
6106       case 3:
6107         /* The original hex closure is
6108 
6109          {c,
6110          f_b, f_t, f_f, f_b, f_r, f_l,
6111          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
6112          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
6113          */
6114         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6115         if (!continuous && d < dim) continue;
6116         /* The SEM order is
6117          Bottom Slice
6118          v_blf, {e^{(k-1)-n}_bf}, v_brf,
6119          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
6120          v_blb, {e_bb}, v_brb,
6121 
6122          Middle Slice (j)
6123          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
6124          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
6125          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
6126 
6127          Top Slice
6128          v_tlf, {e_tf}, v_trf,
6129          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
6130          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
6131          */
6132         if (continuous) {
6133           const PetscInt oc    = 0;
6134           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
6135           const PetscInt oft   = ofb + PetscSqr(k - 1);
6136           const PetscInt off   = oft + PetscSqr(k - 1);
6137           const PetscInt ofk   = off + PetscSqr(k - 1);
6138           const PetscInt ofr   = ofk + PetscSqr(k - 1);
6139           const PetscInt ofl   = ofr + PetscSqr(k - 1);
6140           const PetscInt oebl  = ofl + PetscSqr(k - 1);
6141           const PetscInt oebb  = oebl + (k - 1);
6142           const PetscInt oebr  = oebb + (k - 1);
6143           const PetscInt oebf  = oebr + (k - 1);
6144           const PetscInt oetf  = oebf + (k - 1);
6145           const PetscInt oetr  = oetf + (k - 1);
6146           const PetscInt oetb  = oetr + (k - 1);
6147           const PetscInt oetl  = oetb + (k - 1);
6148           const PetscInt oerf  = oetl + (k - 1);
6149           const PetscInt oelf  = oerf + (k - 1);
6150           const PetscInt oelb  = oelf + (k - 1);
6151           const PetscInt oerb  = oelb + (k - 1);
6152           const PetscInt ovblf = oerb + (k - 1);
6153           const PetscInt ovblb = ovblf + 1;
6154           const PetscInt ovbrb = ovblb + 1;
6155           const PetscInt ovbrf = ovbrb + 1;
6156           const PetscInt ovtlf = ovbrf + 1;
6157           const PetscInt ovtrf = ovtlf + 1;
6158           const PetscInt ovtrb = ovtrf + 1;
6159           const PetscInt ovtlb = ovtrb + 1;
6160           PetscInt       o, n;
6161 
6162           /* Bottom Slice */
6163           /*   bottom */
6164           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
6165           for (o = oetf - 1; o >= oebf; --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] = ovbrf * Nc + c + foffset;
6168           /*   middle */
6169           for (i = 0; i < k - 1; ++i) {
6170             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
6171             for (n = 0; n < k - 1; ++n) {
6172               o = ofb + n * (k - 1) + i;
6173               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6174             }
6175             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
6176           }
6177           /*   top */
6178           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
6179           for (o = oebb; o < oebr; ++o)
6180             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6181           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
6182 
6183           /* Middle Slice */
6184           for (j = 0; j < k - 1; ++j) {
6185             /*   bottom */
6186             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
6187             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
6188               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6189             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
6190             /*   middle */
6191             for (i = 0; i < k - 1; ++i) {
6192               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
6193               for (n = 0; n < k - 1; ++n)
6194                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
6195               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
6196             }
6197             /*   top */
6198             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
6199             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
6200               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6201             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
6202           }
6203 
6204           /* Top Slice */
6205           /*   bottom */
6206           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
6207           for (o = oetf; o < oetr; ++o)
6208             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6209           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
6210           /*   middle */
6211           for (i = 0; i < k - 1; ++i) {
6212             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
6213             for (n = 0; n < k - 1; ++n)
6214               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
6215             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
6216           }
6217           /*   top */
6218           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
6219           for (o = oetl - 1; o >= oetb; --o)
6220             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6221           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
6222 
6223           foffset = offset;
6224         } else {
6225           PetscInt dof;
6226 
6227           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6228           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6229           foffset = offset;
6230         }
6231         break;
6232       default:
6233         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
6234       }
6235     }
6236     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
6237     /* Check permutation */
6238     {
6239       PetscInt *check;
6240 
6241       PetscCall(PetscMalloc1(size, &check));
6242       for (i = 0; i < size; ++i) {
6243         check[i] = -1;
6244         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
6245       }
6246       for (i = 0; i < size; ++i) check[perm[i]] = i;
6247       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
6248       PetscCall(PetscFree(check));
6249     }
6250     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
6251     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
6252       PetscInt *loc_perm;
6253       PetscCall(PetscMalloc1(size * 2, &loc_perm));
6254       for (PetscInt i = 0; i < size; i++) {
6255         loc_perm[i]        = perm[i];
6256         loc_perm[size + i] = size + perm[i];
6257       }
6258       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
6259     }
6260   }
6261   PetscFunctionReturn(PETSC_SUCCESS);
6262 }
6263 
6264 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
6265 {
6266   PetscDS  prob;
6267   PetscInt depth, Nf, h;
6268   DMLabel  label;
6269 
6270   PetscFunctionBeginHot;
6271   PetscCall(DMGetDS(dm, &prob));
6272   Nf      = prob->Nf;
6273   label   = dm->depthLabel;
6274   *dspace = NULL;
6275   if (field < Nf) {
6276     PetscObject disc = prob->disc[field];
6277 
6278     if (disc->classid == PETSCFE_CLASSID) {
6279       PetscDualSpace dsp;
6280 
6281       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
6282       PetscCall(DMLabelGetNumValues(label, &depth));
6283       PetscCall(DMLabelGetValue(label, point, &h));
6284       h = depth - 1 - h;
6285       if (h) {
6286         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
6287       } else {
6288         *dspace = dsp;
6289       }
6290     }
6291   }
6292   PetscFunctionReturn(PETSC_SUCCESS);
6293 }
6294 
6295 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6296 {
6297   PetscScalar       *array;
6298   const PetscScalar *vArray;
6299   const PetscInt    *cone, *coneO;
6300   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
6301 
6302   PetscFunctionBeginHot;
6303   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6304   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6305   PetscCall(DMPlexGetCone(dm, point, &cone));
6306   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6307   if (!values || !*values) {
6308     if ((point >= pStart) && (point < pEnd)) {
6309       PetscInt dof;
6310 
6311       PetscCall(PetscSectionGetDof(section, point, &dof));
6312       size += dof;
6313     }
6314     for (p = 0; p < numPoints; ++p) {
6315       const PetscInt cp = cone[p];
6316       PetscInt       dof;
6317 
6318       if ((cp < pStart) || (cp >= pEnd)) continue;
6319       PetscCall(PetscSectionGetDof(section, cp, &dof));
6320       size += dof;
6321     }
6322     if (!values) {
6323       if (csize) *csize = size;
6324       PetscFunctionReturn(PETSC_SUCCESS);
6325     }
6326     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
6327   } else {
6328     array = *values;
6329   }
6330   size = 0;
6331   PetscCall(VecGetArrayRead(v, &vArray));
6332   if ((point >= pStart) && (point < pEnd)) {
6333     PetscInt           dof, off, d;
6334     const PetscScalar *varr;
6335 
6336     PetscCall(PetscSectionGetDof(section, point, &dof));
6337     PetscCall(PetscSectionGetOffset(section, point, &off));
6338     varr = PetscSafePointerPlusOffset(vArray, off);
6339     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6340     size += dof;
6341   }
6342   for (p = 0; p < numPoints; ++p) {
6343     const PetscInt     cp = cone[p];
6344     PetscInt           o  = coneO[p];
6345     PetscInt           dof, off, d;
6346     const PetscScalar *varr;
6347 
6348     if ((cp < pStart) || (cp >= pEnd)) continue;
6349     PetscCall(PetscSectionGetDof(section, cp, &dof));
6350     PetscCall(PetscSectionGetOffset(section, cp, &off));
6351     varr = PetscSafePointerPlusOffset(vArray, off);
6352     if (o >= 0) {
6353       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6354     } else {
6355       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
6356     }
6357     size += dof;
6358   }
6359   PetscCall(VecRestoreArrayRead(v, &vArray));
6360   if (!*values) {
6361     if (csize) *csize = size;
6362     *values = array;
6363   } else {
6364     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6365     *csize = size;
6366   }
6367   PetscFunctionReturn(PETSC_SUCCESS);
6368 }
6369 
6370 /* Compress out points not in the section */
6371 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
6372 {
6373   const PetscInt np = *numPoints;
6374   PetscInt       pStart, pEnd, p, q;
6375 
6376   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6377   for (p = 0, q = 0; p < np; ++p) {
6378     const PetscInt r = points[p * 2];
6379     if ((r >= pStart) && (r < pEnd)) {
6380       points[q * 2]     = r;
6381       points[q * 2 + 1] = points[p * 2 + 1];
6382       ++q;
6383     }
6384   }
6385   *numPoints = q;
6386   return PETSC_SUCCESS;
6387 }
6388 
6389 /* Compressed closure does not apply closure permutation */
6390 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6391 {
6392   const PetscInt *cla = NULL;
6393   PetscInt        np, *pts = NULL;
6394 
6395   PetscFunctionBeginHot;
6396   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
6397   if (!ornt && *clPoints) {
6398     PetscInt dof, off;
6399 
6400     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
6401     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
6402     PetscCall(ISGetIndices(*clPoints, &cla));
6403     np  = dof / 2;
6404     pts = PetscSafePointerPlusOffset((PetscInt *)cla, off);
6405   } else {
6406     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
6407     PetscCall(CompressPoints_Private(section, &np, pts));
6408   }
6409   *numPoints = np;
6410   *points    = pts;
6411   *clp       = cla;
6412   PetscFunctionReturn(PETSC_SUCCESS);
6413 }
6414 
6415 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6416 {
6417   PetscFunctionBeginHot;
6418   if (!*clPoints) {
6419     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
6420   } else {
6421     PetscCall(ISRestoreIndices(*clPoints, clp));
6422   }
6423   *numPoints = 0;
6424   *points    = NULL;
6425   *clSec     = NULL;
6426   *clPoints  = NULL;
6427   *clp       = NULL;
6428   PetscFunctionReturn(PETSC_SUCCESS);
6429 }
6430 
6431 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
6432 {
6433   PetscInt            offset = 0, p;
6434   const PetscInt    **perms  = NULL;
6435   const PetscScalar **flips  = NULL;
6436 
6437   PetscFunctionBeginHot;
6438   *size = 0;
6439   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6440   for (p = 0; p < numPoints; p++) {
6441     const PetscInt     point = points[2 * p];
6442     const PetscInt    *perm  = perms ? perms[p] : NULL;
6443     const PetscScalar *flip  = flips ? flips[p] : NULL;
6444     PetscInt           dof, off, d;
6445     const PetscScalar *varr;
6446 
6447     PetscCall(PetscSectionGetDof(section, point, &dof));
6448     PetscCall(PetscSectionGetOffset(section, point, &off));
6449     varr = PetscSafePointerPlusOffset(vArray, off);
6450     if (clperm) {
6451       if (perm) {
6452         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
6453       } else {
6454         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
6455       }
6456       if (flip) {
6457         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
6458       }
6459     } else {
6460       if (perm) {
6461         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
6462       } else {
6463         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
6464       }
6465       if (flip) {
6466         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
6467       }
6468     }
6469     offset += dof;
6470   }
6471   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6472   *size = offset;
6473   PetscFunctionReturn(PETSC_SUCCESS);
6474 }
6475 
6476 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[])
6477 {
6478   PetscInt offset = 0, f;
6479 
6480   PetscFunctionBeginHot;
6481   *size = 0;
6482   for (f = 0; f < numFields; ++f) {
6483     PetscInt            p;
6484     const PetscInt    **perms = NULL;
6485     const PetscScalar **flips = NULL;
6486 
6487     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6488     for (p = 0; p < numPoints; p++) {
6489       const PetscInt     point = points[2 * p];
6490       PetscInt           fdof, foff, b;
6491       const PetscScalar *varr;
6492       const PetscInt    *perm = perms ? perms[p] : NULL;
6493       const PetscScalar *flip = flips ? flips[p] : NULL;
6494 
6495       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6496       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6497       varr = &vArray[foff];
6498       if (clperm) {
6499         if (perm) {
6500           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6501         } else {
6502           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6503         }
6504         if (flip) {
6505           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6506         }
6507       } else {
6508         if (perm) {
6509           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6510         } else {
6511           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6512         }
6513         if (flip) {
6514           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6515         }
6516       }
6517       offset += fdof;
6518     }
6519     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6520   }
6521   *size = offset;
6522   PetscFunctionReturn(PETSC_SUCCESS);
6523 }
6524 
6525 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, PetscBool useClPerm, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6526 {
6527   PetscSection    clSection;
6528   IS              clPoints;
6529   PetscInt       *points = NULL;
6530   const PetscInt *clp, *perm = NULL;
6531   PetscInt        depth, numFields, numPoints, asize;
6532 
6533   PetscFunctionBeginHot;
6534   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6535   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6536   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6537   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
6538   PetscCall(DMPlexGetDepth(dm, &depth));
6539   PetscCall(PetscSectionGetNumFields(section, &numFields));
6540   if (depth == 1 && numFields < 2) {
6541     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6542     PetscFunctionReturn(PETSC_SUCCESS);
6543   }
6544   /* Get points */
6545   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6546   /* Get sizes */
6547   asize = 0;
6548   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6549     PetscInt dof;
6550     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6551     asize += dof;
6552   }
6553   if (values) {
6554     const PetscScalar *vArray;
6555     PetscInt           size;
6556 
6557     if (*values) {
6558       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);
6559     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6560     if (useClPerm) PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6561     PetscCall(VecGetArrayRead(v, &vArray));
6562     /* Get values */
6563     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6564     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6565     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6566     /* Cleanup array */
6567     PetscCall(VecRestoreArrayRead(v, &vArray));
6568   }
6569   if (csize) *csize = asize;
6570   /* Cleanup points */
6571   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6572   PetscFunctionReturn(PETSC_SUCCESS);
6573 }
6574 
6575 /*@C
6576   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6577 
6578   Not collective
6579 
6580   Input Parameters:
6581 + dm      - The `DM`
6582 . section - The section describing the layout in `v`, or `NULL` to use the default section
6583 . v       - The local vector
6584 - point   - The point in the `DM`
6585 
6586   Input/Output Parameters:
6587 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6588 - values - An array to use for the values, or *values = `NULL` to have it allocated automatically;
6589            if the user provided `NULL`, it is a borrowed array and should not be freed, use  `DMPlexVecRestoreClosure()` to return it
6590 
6591   Level: intermediate
6592 
6593   Notes:
6594   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6595   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6596   assembly function, and a user may already have allocated storage for this operation.
6597 
6598   A typical use could be
6599 .vb
6600    values = NULL;
6601    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6602    for (cl = 0; cl < clSize; ++cl) {
6603      <Compute on closure>
6604    }
6605    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6606 .ve
6607   or
6608 .vb
6609    PetscMalloc1(clMaxSize, &values);
6610    for (p = pStart; p < pEnd; ++p) {
6611      clSize = clMaxSize;
6612      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6613      for (cl = 0; cl < clSize; ++cl) {
6614        <Compute on closure>
6615      }
6616    }
6617    PetscFree(values);
6618 .ve
6619 
6620   Fortran Notes:
6621   The `csize` argument is not present in the Fortran binding.
6622 
6623   `values` must be declared with
6624 .vb
6625   PetscScalar,dimension(:),pointer   :: values
6626 .ve
6627   and it will be allocated internally by PETSc to hold the values returned
6628 
6629 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6630 @*/
6631 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6632 {
6633   PetscFunctionBeginHot;
6634   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6635   PetscFunctionReturn(PETSC_SUCCESS);
6636 }
6637 
6638 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6639 {
6640   DMLabel            depthLabel;
6641   PetscSection       clSection;
6642   IS                 clPoints;
6643   PetscScalar       *array;
6644   const PetscScalar *vArray;
6645   PetscInt          *points = NULL;
6646   const PetscInt    *clp, *perm = NULL;
6647   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6648 
6649   PetscFunctionBeginHot;
6650   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6651   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6652   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6653   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6654   PetscCall(DMPlexGetDepth(dm, &mdepth));
6655   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6656   PetscCall(PetscSectionGetNumFields(section, &numFields));
6657   if (mdepth == 1 && numFields < 2) {
6658     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6659     PetscFunctionReturn(PETSC_SUCCESS);
6660   }
6661   /* Get points */
6662   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6663   for (clsize = 0, p = 0; p < Np; p++) {
6664     PetscInt dof;
6665     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6666     clsize += dof;
6667   }
6668   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6669   /* Filter points */
6670   for (p = 0; p < numPoints * 2; p += 2) {
6671     PetscInt dep;
6672 
6673     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6674     if (dep != depth) continue;
6675     points[Np * 2 + 0] = points[p];
6676     points[Np * 2 + 1] = points[p + 1];
6677     ++Np;
6678   }
6679   /* Get array */
6680   if (!values || !*values) {
6681     PetscInt asize = 0, dof;
6682 
6683     for (p = 0; p < Np * 2; p += 2) {
6684       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6685       asize += dof;
6686     }
6687     if (!values) {
6688       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6689       if (csize) *csize = asize;
6690       PetscFunctionReturn(PETSC_SUCCESS);
6691     }
6692     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6693   } else {
6694     array = *values;
6695   }
6696   PetscCall(VecGetArrayRead(v, &vArray));
6697   /* Get values */
6698   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6699   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6700   /* Cleanup points */
6701   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6702   /* Cleanup array */
6703   PetscCall(VecRestoreArrayRead(v, &vArray));
6704   if (!*values) {
6705     if (csize) *csize = size;
6706     *values = array;
6707   } else {
6708     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6709     *csize = size;
6710   }
6711   PetscFunctionReturn(PETSC_SUCCESS);
6712 }
6713 
6714 /*@C
6715   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point' obtained with `DMPlexVecGetClosure()`
6716 
6717   Not collective
6718 
6719   Input Parameters:
6720 + dm      - The `DM`
6721 . section - The section describing the layout in `v`, or `NULL` to use the default section
6722 . v       - The local vector
6723 . point   - The point in the `DM`
6724 . csize   - The number of values in the closure, or `NULL`
6725 - values  - The array of values
6726 
6727   Level: intermediate
6728 
6729   Note:
6730   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6731 
6732   Fortran Note:
6733   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6734 
6735 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6736 @*/
6737 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6738 {
6739   PetscInt size = 0;
6740 
6741   PetscFunctionBegin;
6742   /* Should work without recalculating size */
6743   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6744   *values = NULL;
6745   PetscFunctionReturn(PETSC_SUCCESS);
6746 }
6747 
6748 static inline void add(PetscScalar *x, PetscScalar y)
6749 {
6750   *x += y;
6751 }
6752 static inline void insert(PetscScalar *x, PetscScalar y)
6753 {
6754   *x = y;
6755 }
6756 
6757 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[])
6758 {
6759   PetscInt        cdof;  /* The number of constraints on this point */
6760   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6761   PetscScalar    *a;
6762   PetscInt        off, cind = 0, k;
6763 
6764   PetscFunctionBegin;
6765   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6766   PetscCall(PetscSectionGetOffset(section, point, &off));
6767   a = &array[off];
6768   if (!cdof || setBC) {
6769     if (clperm) {
6770       if (perm) {
6771         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6772       } else {
6773         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6774       }
6775     } else {
6776       if (perm) {
6777         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6778       } else {
6779         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6780       }
6781     }
6782   } else {
6783     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6784     if (clperm) {
6785       if (perm) {
6786         for (k = 0; k < dof; ++k) {
6787           if ((cind < cdof) && (k == cdofs[cind])) {
6788             ++cind;
6789             continue;
6790           }
6791           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6792         }
6793       } else {
6794         for (k = 0; k < dof; ++k) {
6795           if ((cind < cdof) && (k == cdofs[cind])) {
6796             ++cind;
6797             continue;
6798           }
6799           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6800         }
6801       }
6802     } else {
6803       if (perm) {
6804         for (k = 0; k < dof; ++k) {
6805           if ((cind < cdof) && (k == cdofs[cind])) {
6806             ++cind;
6807             continue;
6808           }
6809           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6810         }
6811       } else {
6812         for (k = 0; k < dof; ++k) {
6813           if ((cind < cdof) && (k == cdofs[cind])) {
6814             ++cind;
6815             continue;
6816           }
6817           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6818         }
6819       }
6820     }
6821   }
6822   PetscFunctionReturn(PETSC_SUCCESS);
6823 }
6824 
6825 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[])
6826 {
6827   PetscInt        cdof;  /* The number of constraints on this point */
6828   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6829   PetscScalar    *a;
6830   PetscInt        off, cind = 0, k;
6831 
6832   PetscFunctionBegin;
6833   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6834   PetscCall(PetscSectionGetOffset(section, point, &off));
6835   a = &array[off];
6836   if (cdof) {
6837     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6838     if (clperm) {
6839       if (perm) {
6840         for (k = 0; k < dof; ++k) {
6841           if ((cind < cdof) && (k == cdofs[cind])) {
6842             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6843             cind++;
6844           }
6845         }
6846       } else {
6847         for (k = 0; k < dof; ++k) {
6848           if ((cind < cdof) && (k == cdofs[cind])) {
6849             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6850             cind++;
6851           }
6852         }
6853       }
6854     } else {
6855       if (perm) {
6856         for (k = 0; k < dof; ++k) {
6857           if ((cind < cdof) && (k == cdofs[cind])) {
6858             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6859             cind++;
6860           }
6861         }
6862       } else {
6863         for (k = 0; k < dof; ++k) {
6864           if ((cind < cdof) && (k == cdofs[cind])) {
6865             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6866             cind++;
6867           }
6868         }
6869       }
6870     }
6871   }
6872   PetscFunctionReturn(PETSC_SUCCESS);
6873 }
6874 
6875 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[])
6876 {
6877   PetscScalar    *a;
6878   PetscInt        fdof, foff, fcdof, foffset = *offset;
6879   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6880   PetscInt        cind = 0, b;
6881 
6882   PetscFunctionBegin;
6883   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6884   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6885   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6886   a = &array[foff];
6887   if (!fcdof || setBC) {
6888     if (clperm) {
6889       if (perm) {
6890         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6891       } else {
6892         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6893       }
6894     } else {
6895       if (perm) {
6896         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6897       } else {
6898         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6899       }
6900     }
6901   } else {
6902     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6903     if (clperm) {
6904       if (perm) {
6905         for (b = 0; b < fdof; b++) {
6906           if ((cind < fcdof) && (b == fcdofs[cind])) {
6907             ++cind;
6908             continue;
6909           }
6910           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6911         }
6912       } else {
6913         for (b = 0; b < fdof; b++) {
6914           if ((cind < fcdof) && (b == fcdofs[cind])) {
6915             ++cind;
6916             continue;
6917           }
6918           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6919         }
6920       }
6921     } else {
6922       if (perm) {
6923         for (b = 0; b < fdof; b++) {
6924           if ((cind < fcdof) && (b == fcdofs[cind])) {
6925             ++cind;
6926             continue;
6927           }
6928           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6929         }
6930       } else {
6931         for (b = 0; b < fdof; b++) {
6932           if ((cind < fcdof) && (b == fcdofs[cind])) {
6933             ++cind;
6934             continue;
6935           }
6936           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6937         }
6938       }
6939     }
6940   }
6941   *offset += fdof;
6942   PetscFunctionReturn(PETSC_SUCCESS);
6943 }
6944 
6945 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[])
6946 {
6947   PetscScalar    *a;
6948   PetscInt        fdof, foff, fcdof, foffset = *offset;
6949   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6950   PetscInt        Nc, cind = 0, ncind = 0, b;
6951   PetscBool       ncSet, fcSet;
6952 
6953   PetscFunctionBegin;
6954   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6955   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6956   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6957   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6958   a = &array[foff];
6959   if (fcdof) {
6960     /* We just override fcdof and fcdofs with Ncc and comps */
6961     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6962     if (clperm) {
6963       if (perm) {
6964         if (comps) {
6965           for (b = 0; b < fdof; b++) {
6966             ncSet = fcSet = PETSC_FALSE;
6967             if (b % Nc == comps[ncind]) {
6968               ncind = (ncind + 1) % Ncc;
6969               ncSet = PETSC_TRUE;
6970             }
6971             if ((cind < fcdof) && (b == fcdofs[cind])) {
6972               ++cind;
6973               fcSet = PETSC_TRUE;
6974             }
6975             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6976           }
6977         } else {
6978           for (b = 0; b < fdof; b++) {
6979             if ((cind < fcdof) && (b == fcdofs[cind])) {
6980               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6981               ++cind;
6982             }
6983           }
6984         }
6985       } else {
6986         if (comps) {
6987           for (b = 0; b < fdof; b++) {
6988             ncSet = fcSet = PETSC_FALSE;
6989             if (b % Nc == comps[ncind]) {
6990               ncind = (ncind + 1) % Ncc;
6991               ncSet = PETSC_TRUE;
6992             }
6993             if ((cind < fcdof) && (b == fcdofs[cind])) {
6994               ++cind;
6995               fcSet = PETSC_TRUE;
6996             }
6997             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6998           }
6999         } else {
7000           for (b = 0; b < fdof; b++) {
7001             if ((cind < fcdof) && (b == fcdofs[cind])) {
7002               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
7003               ++cind;
7004             }
7005           }
7006         }
7007       }
7008     } else {
7009       if (perm) {
7010         if (comps) {
7011           for (b = 0; b < fdof; b++) {
7012             ncSet = fcSet = PETSC_FALSE;
7013             if (b % Nc == comps[ncind]) {
7014               ncind = (ncind + 1) % Ncc;
7015               ncSet = PETSC_TRUE;
7016             }
7017             if ((cind < fcdof) && (b == fcdofs[cind])) {
7018               ++cind;
7019               fcSet = PETSC_TRUE;
7020             }
7021             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
7022           }
7023         } else {
7024           for (b = 0; b < fdof; b++) {
7025             if ((cind < fcdof) && (b == fcdofs[cind])) {
7026               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
7027               ++cind;
7028             }
7029           }
7030         }
7031       } else {
7032         if (comps) {
7033           for (b = 0; b < fdof; b++) {
7034             ncSet = fcSet = PETSC_FALSE;
7035             if (b % Nc == comps[ncind]) {
7036               ncind = (ncind + 1) % Ncc;
7037               ncSet = PETSC_TRUE;
7038             }
7039             if ((cind < fcdof) && (b == fcdofs[cind])) {
7040               ++cind;
7041               fcSet = PETSC_TRUE;
7042             }
7043             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
7044           }
7045         } else {
7046           for (b = 0; b < fdof; b++) {
7047             if ((cind < fcdof) && (b == fcdofs[cind])) {
7048               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
7049               ++cind;
7050             }
7051           }
7052         }
7053       }
7054     }
7055   }
7056   *offset += fdof;
7057   PetscFunctionReturn(PETSC_SUCCESS);
7058 }
7059 
7060 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7061 {
7062   PetscScalar    *array;
7063   const PetscInt *cone, *coneO;
7064   PetscInt        pStart, pEnd, p, numPoints, off, dof;
7065 
7066   PetscFunctionBeginHot;
7067   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
7068   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
7069   PetscCall(DMPlexGetCone(dm, point, &cone));
7070   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
7071   PetscCall(VecGetArray(v, &array));
7072   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
7073     const PetscInt cp = !p ? point : cone[p - 1];
7074     const PetscInt o  = !p ? 0 : coneO[p - 1];
7075 
7076     if ((cp < pStart) || (cp >= pEnd)) {
7077       dof = 0;
7078       continue;
7079     }
7080     PetscCall(PetscSectionGetDof(section, cp, &dof));
7081     /* ADD_VALUES */
7082     {
7083       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7084       PetscScalar    *a;
7085       PetscInt        cdof, coff, cind = 0, k;
7086 
7087       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
7088       PetscCall(PetscSectionGetOffset(section, cp, &coff));
7089       a = &array[coff];
7090       if (!cdof) {
7091         if (o >= 0) {
7092           for (k = 0; k < dof; ++k) a[k] += values[off + k];
7093         } else {
7094           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
7095         }
7096       } else {
7097         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
7098         if (o >= 0) {
7099           for (k = 0; k < dof; ++k) {
7100             if ((cind < cdof) && (k == cdofs[cind])) {
7101               ++cind;
7102               continue;
7103             }
7104             a[k] += values[off + k];
7105           }
7106         } else {
7107           for (k = 0; k < dof; ++k) {
7108             if ((cind < cdof) && (k == cdofs[cind])) {
7109               ++cind;
7110               continue;
7111             }
7112             a[k] += values[off + dof - k - 1];
7113           }
7114         }
7115       }
7116     }
7117   }
7118   PetscCall(VecRestoreArray(v, &array));
7119   PetscFunctionReturn(PETSC_SUCCESS);
7120 }
7121 
7122 /*@C
7123   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
7124 
7125   Not collective
7126 
7127   Input Parameters:
7128 + dm      - The `DM`
7129 . section - The section describing the layout in `v`, or `NULL` to use the default section
7130 . v       - The local vector
7131 . point   - The point in the `DM`
7132 . values  - The array of values
7133 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
7134             where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
7135 
7136   Level: intermediate
7137 
7138   Note:
7139   Usually the input arrays were obtained with `DMPlexVecGetClosure()`
7140 
7141   Fortran Note:
7142   `values` must be declared with
7143 .vb
7144   PetscScalar,dimension(:),pointer   :: values
7145 .ve
7146 
7147 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
7148 @*/
7149 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7150 {
7151   PetscSection    clSection;
7152   IS              clPoints;
7153   PetscScalar    *array;
7154   PetscInt       *points = NULL;
7155   const PetscInt *clp, *clperm = NULL;
7156   PetscInt        depth, numFields, numPoints, p, clsize;
7157 
7158   PetscFunctionBeginHot;
7159   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7160   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7161   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7162   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7163   PetscCall(DMPlexGetDepth(dm, &depth));
7164   PetscCall(PetscSectionGetNumFields(section, &numFields));
7165   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
7166     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
7167     PetscFunctionReturn(PETSC_SUCCESS);
7168   }
7169   /* Get points */
7170   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7171   for (clsize = 0, p = 0; p < numPoints; p++) {
7172     PetscInt dof;
7173     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7174     clsize += dof;
7175   }
7176   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7177   /* Get array */
7178   PetscCall(VecGetArray(v, &array));
7179   /* Get values */
7180   if (numFields > 0) {
7181     PetscInt offset = 0, f;
7182     for (f = 0; f < numFields; ++f) {
7183       const PetscInt    **perms = NULL;
7184       const PetscScalar **flips = NULL;
7185 
7186       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7187       switch (mode) {
7188       case INSERT_VALUES:
7189         for (p = 0; p < numPoints; p++) {
7190           const PetscInt     point = points[2 * p];
7191           const PetscInt    *perm  = perms ? perms[p] : NULL;
7192           const PetscScalar *flip  = flips ? flips[p] : NULL;
7193           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
7194         }
7195         break;
7196       case INSERT_ALL_VALUES:
7197         for (p = 0; p < numPoints; p++) {
7198           const PetscInt     point = points[2 * p];
7199           const PetscInt    *perm  = perms ? perms[p] : NULL;
7200           const PetscScalar *flip  = flips ? flips[p] : NULL;
7201           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
7202         }
7203         break;
7204       case INSERT_BC_VALUES:
7205         for (p = 0; p < numPoints; p++) {
7206           const PetscInt     point = points[2 * p];
7207           const PetscInt    *perm  = perms ? perms[p] : NULL;
7208           const PetscScalar *flip  = flips ? flips[p] : NULL;
7209           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
7210         }
7211         break;
7212       case ADD_VALUES:
7213         for (p = 0; p < numPoints; p++) {
7214           const PetscInt     point = points[2 * p];
7215           const PetscInt    *perm  = perms ? perms[p] : NULL;
7216           const PetscScalar *flip  = flips ? flips[p] : NULL;
7217           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
7218         }
7219         break;
7220       case ADD_ALL_VALUES:
7221         for (p = 0; p < numPoints; p++) {
7222           const PetscInt     point = points[2 * p];
7223           const PetscInt    *perm  = perms ? perms[p] : NULL;
7224           const PetscScalar *flip  = flips ? flips[p] : NULL;
7225           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
7226         }
7227         break;
7228       case ADD_BC_VALUES:
7229         for (p = 0; p < numPoints; p++) {
7230           const PetscInt     point = points[2 * p];
7231           const PetscInt    *perm  = perms ? perms[p] : NULL;
7232           const PetscScalar *flip  = flips ? flips[p] : NULL;
7233           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
7234         }
7235         break;
7236       default:
7237         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7238       }
7239       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7240     }
7241   } else {
7242     PetscInt            dof, off;
7243     const PetscInt    **perms = NULL;
7244     const PetscScalar **flips = NULL;
7245 
7246     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
7247     switch (mode) {
7248     case INSERT_VALUES:
7249       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7250         const PetscInt     point = points[2 * p];
7251         const PetscInt    *perm  = perms ? perms[p] : NULL;
7252         const PetscScalar *flip  = flips ? flips[p] : NULL;
7253         PetscCall(PetscSectionGetDof(section, point, &dof));
7254         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
7255       }
7256       break;
7257     case INSERT_ALL_VALUES:
7258       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7259         const PetscInt     point = points[2 * p];
7260         const PetscInt    *perm  = perms ? perms[p] : NULL;
7261         const PetscScalar *flip  = flips ? flips[p] : NULL;
7262         PetscCall(PetscSectionGetDof(section, point, &dof));
7263         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
7264       }
7265       break;
7266     case INSERT_BC_VALUES:
7267       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7268         const PetscInt     point = points[2 * p];
7269         const PetscInt    *perm  = perms ? perms[p] : NULL;
7270         const PetscScalar *flip  = flips ? flips[p] : NULL;
7271         PetscCall(PetscSectionGetDof(section, point, &dof));
7272         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
7273       }
7274       break;
7275     case ADD_VALUES:
7276       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7277         const PetscInt     point = points[2 * p];
7278         const PetscInt    *perm  = perms ? perms[p] : NULL;
7279         const PetscScalar *flip  = flips ? flips[p] : NULL;
7280         PetscCall(PetscSectionGetDof(section, point, &dof));
7281         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
7282       }
7283       break;
7284     case ADD_ALL_VALUES:
7285       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7286         const PetscInt     point = points[2 * p];
7287         const PetscInt    *perm  = perms ? perms[p] : NULL;
7288         const PetscScalar *flip  = flips ? flips[p] : NULL;
7289         PetscCall(PetscSectionGetDof(section, point, &dof));
7290         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
7291       }
7292       break;
7293     case ADD_BC_VALUES:
7294       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7295         const PetscInt     point = points[2 * p];
7296         const PetscInt    *perm  = perms ? perms[p] : NULL;
7297         const PetscScalar *flip  = flips ? flips[p] : NULL;
7298         PetscCall(PetscSectionGetDof(section, point, &dof));
7299         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
7300       }
7301       break;
7302     default:
7303       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7304     }
7305     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
7306   }
7307   /* Cleanup points */
7308   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7309   /* Cleanup array */
7310   PetscCall(VecRestoreArray(v, &array));
7311   PetscFunctionReturn(PETSC_SUCCESS);
7312 }
7313 
7314 /* Check whether the given point is in the label. If not, update the offset to skip this point */
7315 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
7316 {
7317   PetscFunctionBegin;
7318   *contains = PETSC_TRUE;
7319   if (label) {
7320     PetscInt fdof;
7321 
7322     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
7323     if (!*contains) {
7324       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7325       *offset += fdof;
7326       PetscFunctionReturn(PETSC_SUCCESS);
7327     }
7328   }
7329   PetscFunctionReturn(PETSC_SUCCESS);
7330 }
7331 
7332 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
7333 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)
7334 {
7335   PetscSection    clSection;
7336   IS              clPoints;
7337   PetscScalar    *array;
7338   PetscInt       *points = NULL;
7339   const PetscInt *clp;
7340   PetscInt        numFields, numPoints, p;
7341   PetscInt        offset = 0, f;
7342 
7343   PetscFunctionBeginHot;
7344   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7345   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7346   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7347   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7348   PetscCall(PetscSectionGetNumFields(section, &numFields));
7349   /* Get points */
7350   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7351   /* Get array */
7352   PetscCall(VecGetArray(v, &array));
7353   /* Get values */
7354   for (f = 0; f < numFields; ++f) {
7355     const PetscInt    **perms = NULL;
7356     const PetscScalar **flips = NULL;
7357     PetscBool           contains;
7358 
7359     if (!fieldActive[f]) {
7360       for (p = 0; p < numPoints * 2; p += 2) {
7361         PetscInt fdof;
7362         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7363         offset += fdof;
7364       }
7365       continue;
7366     }
7367     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7368     switch (mode) {
7369     case INSERT_VALUES:
7370       for (p = 0; p < numPoints; p++) {
7371         const PetscInt     point = points[2 * p];
7372         const PetscInt    *perm  = perms ? perms[p] : NULL;
7373         const PetscScalar *flip  = flips ? flips[p] : NULL;
7374         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7375         if (!contains) continue;
7376         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
7377       }
7378       break;
7379     case INSERT_ALL_VALUES:
7380       for (p = 0; p < numPoints; p++) {
7381         const PetscInt     point = points[2 * p];
7382         const PetscInt    *perm  = perms ? perms[p] : NULL;
7383         const PetscScalar *flip  = flips ? flips[p] : NULL;
7384         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7385         if (!contains) continue;
7386         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
7387       }
7388       break;
7389     case INSERT_BC_VALUES:
7390       for (p = 0; p < numPoints; p++) {
7391         const PetscInt     point = points[2 * p];
7392         const PetscInt    *perm  = perms ? perms[p] : NULL;
7393         const PetscScalar *flip  = flips ? flips[p] : NULL;
7394         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7395         if (!contains) continue;
7396         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
7397       }
7398       break;
7399     case ADD_VALUES:
7400       for (p = 0; p < numPoints; p++) {
7401         const PetscInt     point = points[2 * p];
7402         const PetscInt    *perm  = perms ? perms[p] : NULL;
7403         const PetscScalar *flip  = flips ? flips[p] : NULL;
7404         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7405         if (!contains) continue;
7406         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
7407       }
7408       break;
7409     case ADD_ALL_VALUES:
7410       for (p = 0; p < numPoints; p++) {
7411         const PetscInt     point = points[2 * p];
7412         const PetscInt    *perm  = perms ? perms[p] : NULL;
7413         const PetscScalar *flip  = flips ? flips[p] : NULL;
7414         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7415         if (!contains) continue;
7416         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
7417       }
7418       break;
7419     default:
7420       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7421     }
7422     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7423   }
7424   /* Cleanup points */
7425   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7426   /* Cleanup array */
7427   PetscCall(VecRestoreArray(v, &array));
7428   PetscFunctionReturn(PETSC_SUCCESS);
7429 }
7430 
7431 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
7432 {
7433   PetscMPIInt rank;
7434   PetscInt    i, j;
7435 
7436   PetscFunctionBegin;
7437   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7438   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
7439   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
7440   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
7441   numCIndices = numCIndices ? numCIndices : numRIndices;
7442   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
7443   for (i = 0; i < numRIndices; i++) {
7444     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
7445     for (j = 0; j < numCIndices; j++) {
7446 #if defined(PETSC_USE_COMPLEX)
7447       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
7448 #else
7449       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
7450 #endif
7451     }
7452     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
7453   }
7454   PetscFunctionReturn(PETSC_SUCCESS);
7455 }
7456 
7457 /*
7458   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
7459 
7460   Input Parameters:
7461 + section - The section for this data layout
7462 . islocal - Is the section (and thus indices being requested) local or global?
7463 . point   - The point contributing dofs with these indices
7464 . off     - The global offset of this point
7465 . loff    - The local offset of each field
7466 . setBC   - The flag determining whether to include indices of boundary values
7467 . perm    - A permutation of the dofs on this point, or NULL
7468 - indperm - A permutation of the entire indices array, or NULL
7469 
7470   Output Parameter:
7471 . indices - Indices for dofs on this point
7472 
7473   Level: developer
7474 
7475   Note: The indices could be local or global, depending on the value of 'off'.
7476 */
7477 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7478 {
7479   PetscInt        dof;   /* The number of unknowns on this point */
7480   PetscInt        cdof;  /* The number of constraints on this point */
7481   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7482   PetscInt        cind = 0, k;
7483 
7484   PetscFunctionBegin;
7485   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7486   PetscCall(PetscSectionGetDof(section, point, &dof));
7487   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7488   if (!cdof || setBC) {
7489     for (k = 0; k < dof; ++k) {
7490       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7491       const PetscInt ind    = indperm ? indperm[preind] : preind;
7492 
7493       indices[ind] = off + k;
7494     }
7495   } else {
7496     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7497     for (k = 0; k < dof; ++k) {
7498       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7499       const PetscInt ind    = indperm ? indperm[preind] : preind;
7500 
7501       if ((cind < cdof) && (k == cdofs[cind])) {
7502         /* Insert check for returning constrained indices */
7503         indices[ind] = -(off + k + 1);
7504         ++cind;
7505       } else {
7506         indices[ind] = off + k - (islocal ? 0 : cind);
7507       }
7508     }
7509   }
7510   *loff += dof;
7511   PetscFunctionReturn(PETSC_SUCCESS);
7512 }
7513 
7514 /*
7515  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7516 
7517  Input Parameters:
7518 + section - a section (global or local)
7519 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7520 . point - point within section
7521 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7522 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7523 . setBC - identify constrained (boundary condition) points via involution.
7524 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7525 . permsoff - offset
7526 - indperm - index permutation
7527 
7528  Output Parameter:
7529 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7530 . indices - array to hold indices (as defined by section) of each dof associated with point
7531 
7532  Notes:
7533  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7534  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7535  in the local vector.
7536 
7537  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7538  significant).  It is invalid to call with a global section and setBC=true.
7539 
7540  Developer Note:
7541  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7542  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7543  offset could be obtained from the section instead of passing it explicitly as we do now.
7544 
7545  Example:
7546  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7547  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7548  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7549  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.
7550 
7551  Level: developer
7552 */
7553 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[])
7554 {
7555   PetscInt numFields, foff, f;
7556 
7557   PetscFunctionBegin;
7558   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7559   PetscCall(PetscSectionGetNumFields(section, &numFields));
7560   for (f = 0, foff = 0; f < numFields; ++f) {
7561     PetscInt        fdof, cfdof;
7562     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7563     PetscInt        cind = 0, b;
7564     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7565 
7566     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7567     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7568     if (!cfdof || setBC) {
7569       for (b = 0; b < fdof; ++b) {
7570         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7571         const PetscInt ind    = indperm ? indperm[preind] : preind;
7572 
7573         indices[ind] = off + foff + b;
7574       }
7575     } else {
7576       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7577       for (b = 0; b < fdof; ++b) {
7578         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7579         const PetscInt ind    = indperm ? indperm[preind] : preind;
7580 
7581         if ((cind < cfdof) && (b == fcdofs[cind])) {
7582           indices[ind] = -(off + foff + b + 1);
7583           ++cind;
7584         } else {
7585           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7586         }
7587       }
7588     }
7589     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7590     foffs[f] += fdof;
7591   }
7592   PetscFunctionReturn(PETSC_SUCCESS);
7593 }
7594 
7595 /*
7596   This version believes the globalSection offsets for each field, rather than just the point offset
7597 
7598  . foffs - The offset into 'indices' for each field, since it is segregated by field
7599 
7600  Notes:
7601  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7602  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7603 */
7604 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7605 {
7606   PetscInt numFields, foff, f;
7607 
7608   PetscFunctionBegin;
7609   PetscCall(PetscSectionGetNumFields(section, &numFields));
7610   for (f = 0; f < numFields; ++f) {
7611     PetscInt        fdof, cfdof;
7612     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7613     PetscInt        cind = 0, b;
7614     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7615 
7616     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7617     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7618     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7619     if (!cfdof) {
7620       for (b = 0; b < fdof; ++b) {
7621         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7622         const PetscInt ind    = indperm ? indperm[preind] : preind;
7623 
7624         indices[ind] = foff + b;
7625       }
7626     } else {
7627       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7628       for (b = 0; b < fdof; ++b) {
7629         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7630         const PetscInt ind    = indperm ? indperm[preind] : preind;
7631 
7632         if ((cind < cfdof) && (b == fcdofs[cind])) {
7633           indices[ind] = -(foff + b + 1);
7634           ++cind;
7635         } else {
7636           indices[ind] = foff + b - cind;
7637         }
7638       }
7639     }
7640     foffs[f] += fdof;
7641   }
7642   PetscFunctionReturn(PETSC_SUCCESS);
7643 }
7644 
7645 static PetscErrorCode DMPlexAnchorsGetSubMatIndices(PetscInt nPoints, const PetscInt pnts[], PetscSection section, PetscSection cSec, PetscInt tmpIndices[], PetscInt fieldOffsets[], PetscInt indices[], const PetscInt ***perms)
7646 {
7647   PetscInt numFields, sStart, sEnd, cStart, cEnd;
7648 
7649   PetscFunctionBegin;
7650   PetscCall(PetscSectionGetNumFields(section, &numFields));
7651   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7652   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7653   for (PetscInt p = 0; p < nPoints; p++) {
7654     PetscInt     b       = pnts[2 * p];
7655     PetscInt     bSecDof = 0, bOff;
7656     PetscInt     cSecDof = 0;
7657     PetscSection indices_section;
7658 
7659     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7660     if (!bSecDof) continue;
7661     if (b >= cStart && b < cEnd) PetscCall(PetscSectionGetDof(cSec, b, &cSecDof));
7662     indices_section = cSecDof > 0 ? cSec : section;
7663     if (numFields) {
7664       PetscInt fStart[32], fEnd[32];
7665 
7666       fStart[0] = 0;
7667       fEnd[0]   = 0;
7668       for (PetscInt f = 0; f < numFields; f++) {
7669         PetscInt fDof = 0;
7670 
7671         PetscCall(PetscSectionGetFieldDof(indices_section, b, f, &fDof));
7672         fStart[f + 1] = fStart[f] + fDof;
7673         fEnd[f + 1]   = fStart[f + 1];
7674       }
7675       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7676       // only apply permutations on one side
7677       PetscCall(DMPlexGetIndicesPointFields_Internal(indices_section, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, perms ? p : -1, NULL, tmpIndices));
7678       for (PetscInt f = 0; f < numFields; f++) {
7679         for (PetscInt i = fStart[f]; i < fEnd[f]; i++) { indices[fieldOffsets[f]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1); }
7680       }
7681     } else {
7682       PetscInt bEnd = 0;
7683 
7684       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7685       PetscCall(DMPlexGetIndicesPoint_Internal(indices_section, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, tmpIndices));
7686 
7687       for (PetscInt i = 0; i < bEnd; i++) indices[fieldOffsets[0]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1);
7688     }
7689   }
7690   PetscFunctionReturn(PETSC_SUCCESS);
7691 }
7692 
7693 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[])
7694 {
7695   Mat             cMat;
7696   PetscSection    aSec, cSec;
7697   IS              aIS;
7698   PetscInt        aStart = -1, aEnd = -1;
7699   PetscInt        sStart = -1, sEnd = -1;
7700   PetscInt        cStart = -1, cEnd = -1;
7701   const PetscInt *anchors;
7702   PetscInt        numFields, p;
7703   PetscInt        newNumPoints = 0, newNumIndices = 0;
7704   PetscInt       *newPoints, *indices, *newIndices, *tmpIndices, *tmpNewIndices;
7705   PetscInt        oldOffsets[32];
7706   PetscInt        newOffsets[32];
7707   PetscInt        oldOffsetsCopy[32];
7708   PetscInt        newOffsetsCopy[32];
7709   PetscScalar    *modMat         = NULL;
7710   PetscBool       anyConstrained = PETSC_FALSE;
7711 
7712   PetscFunctionBegin;
7713   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7714   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7715   PetscCall(PetscSectionGetNumFields(section, &numFields));
7716 
7717   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7718   /* if there are point-to-point constraints */
7719   if (aSec) {
7720     PetscCall(PetscArrayzero(newOffsets, 32));
7721     PetscCall(PetscArrayzero(oldOffsets, 32));
7722     PetscCall(ISGetIndices(aIS, &anchors));
7723     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7724     PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7725     /* figure out how many points are going to be in the new element matrix
7726      * (we allow double counting, because it's all just going to be summed
7727      * into the global matrix anyway) */
7728     for (p = 0; p < 2 * numPoints; p += 2) {
7729       PetscInt b    = points[p];
7730       PetscInt bDof = 0, bSecDof = 0;
7731 
7732       if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7733       if (!bSecDof) continue;
7734 
7735       for (PetscInt f = 0; f < numFields; f++) {
7736         PetscInt fDof = 0;
7737 
7738         PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7739         oldOffsets[f + 1] += fDof;
7740       }
7741       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7742       if (bDof) {
7743         /* this point is constrained */
7744         /* it is going to be replaced by its anchors */
7745         PetscInt bOff, q;
7746 
7747         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7748         for (q = 0; q < bDof; q++) {
7749           PetscInt a    = anchors[bOff + q];
7750           PetscInt aDof = 0;
7751 
7752           if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7753           if (aDof) {
7754             anyConstrained = PETSC_TRUE;
7755             newNumPoints += 1;
7756           }
7757           newNumIndices += aDof;
7758           for (PetscInt f = 0; f < numFields; ++f) {
7759             PetscInt fDof = 0;
7760 
7761             if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7762             newOffsets[f + 1] += fDof;
7763           }
7764         }
7765       } else {
7766         /* this point is not constrained */
7767         newNumPoints++;
7768         newNumIndices += bSecDof;
7769         for (PetscInt f = 0; f < numFields; ++f) {
7770           PetscInt fDof;
7771 
7772           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7773           newOffsets[f + 1] += fDof;
7774         }
7775       }
7776     }
7777   }
7778   if (!anyConstrained) {
7779     if (outNumPoints) *outNumPoints = 0;
7780     if (outNumIndices) *outNumIndices = 0;
7781     if (outPoints) *outPoints = NULL;
7782     if (outMat) *outMat = NULL;
7783     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7784     PetscFunctionReturn(PETSC_SUCCESS);
7785   }
7786 
7787   if (outNumPoints) *outNumPoints = newNumPoints;
7788   if (outNumIndices) *outNumIndices = newNumIndices;
7789 
7790   for (PetscInt f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7791   for (PetscInt f = 0; f < numFields; ++f) oldOffsets[f + 1] += oldOffsets[f];
7792 
7793   if (!outPoints && !outMat) {
7794     if (offsets) {
7795       for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7796     }
7797     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7798     PetscFunctionReturn(PETSC_SUCCESS);
7799   }
7800 
7801   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7802   PetscCheck(!numFields || oldOffsets[numFields] == numIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, oldOffsets[numFields], numIndices);
7803 
7804   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7805   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7806 
7807   /* output arrays */
7808   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7809   PetscCall(PetscArrayzero(newPoints, 2 * newNumPoints));
7810 
7811   // get the new Points
7812   for (PetscInt p = 0, newP = 0; p < numPoints; p++) {
7813     PetscInt b    = points[2 * p];
7814     PetscInt bDof = 0, bSecDof = 0, bOff;
7815 
7816     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7817     if (!bSecDof) continue;
7818     if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7819     if (bDof) {
7820       PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7821       for (PetscInt q = 0; q < bDof; q++) {
7822         PetscInt a = anchors[bOff + q], aDof = 0;
7823 
7824         if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7825         if (aDof) {
7826           newPoints[2 * newP]     = a;
7827           newPoints[2 * newP + 1] = 0; // orientations are accounted for in constructing the matrix, newly added points are in default orientation
7828           newP++;
7829         }
7830       }
7831     } else {
7832       newPoints[2 * newP]     = b;
7833       newPoints[2 * newP + 1] = points[2 * p + 1];
7834       newP++;
7835     }
7836   }
7837 
7838   if (outMat) {
7839     PetscScalar *tmpMat;
7840     PetscCall(PetscArraycpy(oldOffsetsCopy, oldOffsets, 32));
7841     PetscCall(PetscArraycpy(newOffsetsCopy, newOffsets, 32));
7842 
7843     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &indices));
7844     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7845     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7846     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7847 
7848     for (PetscInt i = 0; i < numIndices; i++) indices[i] = -1;
7849     for (PetscInt i = 0; i < newNumIndices; i++) newIndices[i] = -1;
7850 
7851     PetscCall(DMPlexAnchorsGetSubMatIndices(numPoints, points, section, cSec, tmpIndices, oldOffsetsCopy, indices, perms));
7852     PetscCall(DMPlexAnchorsGetSubMatIndices(newNumPoints, newPoints, section, section, tmpNewIndices, newOffsetsCopy, newIndices, NULL));
7853 
7854     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7855     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7856     PetscCall(PetscArrayzero(modMat, newNumIndices * numIndices));
7857     // for each field, insert the anchor modification into modMat
7858     for (PetscInt f = 0; f < PetscMax(1, numFields); f++) {
7859       PetscInt fStart    = oldOffsets[f];
7860       PetscInt fNewStart = newOffsets[f];
7861       for (PetscInt p = 0, newP = 0, o = fStart, oNew = fNewStart; p < numPoints; p++) {
7862         PetscInt b    = points[2 * p];
7863         PetscInt bDof = 0, bSecDof = 0, bOff;
7864 
7865         if (b >= sStart && b < sEnd) {
7866           if (numFields) {
7867             PetscCall(PetscSectionGetFieldDof(section, b, f, &bSecDof));
7868           } else {
7869             PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7870           }
7871         }
7872         if (!bSecDof) continue;
7873         if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7874         if (bDof) {
7875           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7876           for (PetscInt q = 0; q < bDof; q++, newP++) {
7877             PetscInt a = anchors[bOff + q], aDof = 0;
7878 
7879             if (a >= sStart && a < sEnd) {
7880               if (numFields) {
7881                 PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
7882               } else {
7883                 PetscCall(PetscSectionGetDof(section, a, &aDof));
7884               }
7885             }
7886             if (aDof) {
7887               PetscCall(MatGetValues(cMat, bSecDof, &indices[o], aDof, &newIndices[oNew], tmpMat));
7888               for (PetscInt d = 0; d < bSecDof; d++) {
7889                 for (PetscInt e = 0; e < aDof; e++) modMat[(o + d) * newNumIndices + oNew + e] = tmpMat[d * aDof + e];
7890               }
7891             }
7892             oNew += aDof;
7893           }
7894         } else {
7895           // Insert the identity matrix in this block
7896           for (PetscInt d = 0; d < bSecDof; d++) modMat[(o + d) * newNumIndices + oNew + d] = 1;
7897           oNew += bSecDof;
7898           newP++;
7899         }
7900         o += bSecDof;
7901       }
7902     }
7903 
7904     *outMat = modMat;
7905 
7906     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7907     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7908     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7909     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7910     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &indices));
7911   }
7912   PetscCall(ISRestoreIndices(aIS, &anchors));
7913 
7914   /* output */
7915   if (outPoints) {
7916     *outPoints = newPoints;
7917   } else {
7918     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7919   }
7920   for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7921   PetscFunctionReturn(PETSC_SUCCESS);
7922 }
7923 
7924 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)
7925 {
7926   PetscScalar *modMat        = NULL;
7927   PetscInt     newNumIndices = -1;
7928 
7929   PetscFunctionBegin;
7930   /* 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.
7931      modMat is that matrix C */
7932   PetscCall(DMPlexAnchorsGetSubMatModification(dm, section, numPoints, numIndices, points, perms, outNumPoints, &newNumIndices, outPoints, offsets, outValues ? &modMat : NULL));
7933   if (outNumIndices) *outNumIndices = newNumIndices;
7934   if (modMat) {
7935     const PetscScalar *newValues = values;
7936 
7937     if (multiplyRight) {
7938       PetscScalar *newNewValues = NULL;
7939       PetscBLASInt M, N, K;
7940       PetscScalar  a = 1.0, b = 0.0;
7941 
7942       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);
7943 
7944       PetscCall(PetscBLASIntCast(newNumIndices, &M));
7945       PetscCall(PetscBLASIntCast(numRows, &N));
7946       PetscCall(PetscBLASIntCast(numIndices, &K));
7947       PetscCall(DMGetWorkArray(dm, numRows * newNumIndices, MPIU_SCALAR, &newNewValues));
7948       // row-major to column-major conversion, right multiplication becomes left multiplication
7949       PetscCallBLAS("BLASgemm", BLASgemm_("N", "N", &M, &N, &K, &a, modMat, &M, newValues, &K, &b, newNewValues, &M));
7950       numCols   = newNumIndices;
7951       newValues = newNewValues;
7952     }
7953 
7954     if (multiplyLeft) {
7955       PetscScalar *newNewValues = NULL;
7956       PetscBLASInt M, N, K;
7957       PetscScalar  a = 1.0, b = 0.0;
7958 
7959       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);
7960 
7961       PetscCall(PetscBLASIntCast(numCols, &M));
7962       PetscCall(PetscBLASIntCast(newNumIndices, &N));
7963       PetscCall(PetscBLASIntCast(numIndices, &K));
7964       PetscCall(DMGetWorkArray(dm, newNumIndices * numCols, MPIU_SCALAR, &newNewValues));
7965       // row-major to column-major conversion, left multiplication becomes right multiplication
7966       PetscCallBLAS("BLASgemm", BLASgemm_("N", "T", &M, &N, &K, &a, newValues, &M, modMat, &N, &b, newNewValues, &M));
7967       if (newValues != values) PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &newValues));
7968       newValues = newNewValues;
7969     }
7970     *outValues = (PetscScalar *)newValues;
7971     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7972   }
7973   PetscFunctionReturn(PETSC_SUCCESS);
7974 }
7975 
7976 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)
7977 {
7978   PetscFunctionBegin;
7979   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, numPoints, numIndices, points, perms, numIndices, numIndices, values, outNumPoints, outNumIndices, outPoints, outValues, offsets, PETSC_TRUE, multiplyLeft));
7980   PetscFunctionReturn(PETSC_SUCCESS);
7981 }
7982 
7983 static PetscErrorCode DMPlexGetClosureIndicesSize_Internal(DM dm, PetscSection section, PetscInt point, PetscInt *closureSize)
7984 {
7985   /* Closure ordering */
7986   PetscSection    clSection;
7987   IS              clPoints;
7988   const PetscInt *clp;
7989   PetscInt       *points;
7990   PetscInt        Ncl, Ni = 0;
7991 
7992   PetscFunctionBeginHot;
7993   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7994   for (PetscInt p = 0; p < Ncl * 2; p += 2) {
7995     PetscInt dof;
7996 
7997     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7998     Ni += dof;
7999   }
8000   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8001   *closureSize = Ni;
8002   PetscFunctionReturn(PETSC_SUCCESS);
8003 }
8004 
8005 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)
8006 {
8007   /* Closure ordering */
8008   PetscSection    clSection;
8009   IS              clPoints;
8010   const PetscInt *clp;
8011   PetscInt       *points;
8012   const PetscInt *clperm = NULL;
8013   /* Dof permutation and sign flips */
8014   const PetscInt    **perms[32] = {NULL};
8015   const PetscScalar **flips[32] = {NULL};
8016   PetscScalar        *valCopy   = NULL;
8017   /* Hanging node constraints */
8018   PetscInt    *pointsC = NULL;
8019   PetscScalar *valuesC = NULL;
8020   PetscInt     NclC, NiC;
8021 
8022   PetscInt *idx;
8023   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
8024   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
8025   PetscInt  idxStart, idxEnd;
8026   PetscInt  nRows, nCols;
8027 
8028   PetscFunctionBeginHot;
8029   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8030   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8031   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
8032   PetscAssertPointer(numRows, 6);
8033   PetscAssertPointer(numCols, 7);
8034   if (indices) PetscAssertPointer(indices, 8);
8035   if (outOffsets) PetscAssertPointer(outOffsets, 9);
8036   if (values) PetscAssertPointer(values, 10);
8037   PetscCall(PetscSectionGetNumFields(section, &Nf));
8038   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
8039   PetscCall(PetscArrayzero(offsets, 32));
8040   /* 1) Get points in closure */
8041   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
8042   if (useClPerm) {
8043     PetscInt depth, clsize;
8044     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
8045     for (clsize = 0, p = 0; p < Ncl; p++) {
8046       PetscInt dof;
8047       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
8048       clsize += dof;
8049     }
8050     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
8051   }
8052   /* 2) Get number of indices on these points and field offsets from section */
8053   for (p = 0; p < Ncl * 2; p += 2) {
8054     PetscInt dof, fdof;
8055 
8056     PetscCall(PetscSectionGetDof(section, points[p], &dof));
8057     for (f = 0; f < Nf; ++f) {
8058       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
8059       offsets[f + 1] += fdof;
8060     }
8061     Ni += dof;
8062   }
8063   if (*numRows == -1) *numRows = Ni;
8064   if (*numCols == -1) *numCols = Ni;
8065   nRows = *numRows;
8066   nCols = *numCols;
8067   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
8068   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
8069   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
8070   if (multiplyRight) PetscCheck(nCols == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " columns, got %" PetscInt_FMT, Ni, nCols);
8071   if (multiplyLeft) PetscCheck(nRows == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " rows, got %" PetscInt_FMT, Ni, nRows);
8072   for (f = 0; f < PetscMax(1, Nf); ++f) {
8073     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8074     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
8075     /* may need to apply sign changes to the element matrix */
8076     if (values && flips[f]) {
8077       PetscInt foffset = offsets[f];
8078 
8079       for (p = 0; p < Ncl; ++p) {
8080         PetscInt           pnt  = points[2 * p], fdof;
8081         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
8082 
8083         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
8084         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
8085         if (flip) {
8086           PetscInt i, j, k;
8087 
8088           if (!valCopy) {
8089             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8090             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
8091             *values = valCopy;
8092           }
8093           for (i = 0; i < fdof; ++i) {
8094             PetscScalar fval = flip[i];
8095 
8096             if (multiplyRight) {
8097               for (k = 0; k < nRows; ++k) { valCopy[Ni * k + (foffset + i)] *= fval; }
8098             }
8099             if (multiplyLeft) {
8100               for (k = 0; k < nCols; ++k) { valCopy[nCols * (foffset + i) + k] *= fval; }
8101             }
8102           }
8103         }
8104         foffset += fdof;
8105       }
8106     }
8107   }
8108   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
8109   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, Ncl, Ni, points, perms, nRows, nCols, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, multiplyRight, multiplyLeft));
8110   if (NclC) {
8111     if (multiplyRight) *numCols = NiC;
8112     if (multiplyLeft) *numRows = NiC;
8113     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8114     for (f = 0; f < PetscMax(1, Nf); ++f) {
8115       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8116       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8117     }
8118     for (f = 0; f < PetscMax(1, Nf); ++f) {
8119       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
8120       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
8121     }
8122     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8123     Ncl    = NclC;
8124     Ni     = NiC;
8125     points = pointsC;
8126     if (values) *values = valuesC;
8127   }
8128   /* 5) Calculate indices */
8129   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
8130   PetscCall(PetscSectionGetChart(idxSection, &idxStart, &idxEnd));
8131   if (Nf) {
8132     PetscInt  idxOff;
8133     PetscBool useFieldOffsets;
8134 
8135     if (outOffsets) {
8136       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
8137     }
8138     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
8139     if (useFieldOffsets) {
8140       for (p = 0; p < Ncl; ++p) {
8141         const PetscInt pnt = points[p * 2];
8142 
8143         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
8144       }
8145     } else {
8146       for (p = 0; p < Ncl; ++p) {
8147         const PetscInt pnt = points[p * 2];
8148 
8149         if (pnt < idxStart || pnt >= idxEnd) continue;
8150         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8151         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8152          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
8153          * global section. */
8154         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
8155       }
8156     }
8157   } else {
8158     PetscInt off = 0, idxOff;
8159 
8160     for (p = 0; p < Ncl; ++p) {
8161       const PetscInt  pnt  = points[p * 2];
8162       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
8163 
8164       if (pnt < idxStart || pnt >= idxEnd) continue;
8165       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8166       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8167        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
8168       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
8169     }
8170   }
8171   /* 6) Cleanup */
8172   for (f = 0; f < PetscMax(1, Nf); ++f) {
8173     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8174     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8175   }
8176   if (NclC) {
8177     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
8178   } else {
8179     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8180   }
8181 
8182   if (indices) *indices = idx;
8183   PetscFunctionReturn(PETSC_SUCCESS);
8184 }
8185 
8186 /*@C
8187   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
8188 
8189   Not collective
8190 
8191   Input Parameters:
8192 + dm         - The `DM`
8193 . section    - The `PetscSection` describing the points (a local section)
8194 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8195 . point      - The point defining the closure
8196 - useClPerm  - Use the closure point permutation if available
8197 
8198   Output Parameters:
8199 + numIndices - The number of dof indices in the closure of point with the input sections
8200 . indices    - The dof indices
8201 . outOffsets - Array to write the field offsets into, or `NULL`
8202 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8203 
8204   Level: advanced
8205 
8206   Notes:
8207   Call `DMPlexRestoreClosureIndices()` to free allocated memory
8208 
8209   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8210   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
8211   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8212   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
8213   indices (with the above semantics) are implied.
8214 
8215 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
8216           `PetscSection`, `DMGetGlobalSection()`
8217 @*/
8218 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8219 {
8220   PetscInt numRows = -1, numCols = -1;
8221 
8222   PetscFunctionBeginHot;
8223   PetscCall(DMPlexGetClosureIndices_Internal(dm, section, idxSection, point, useClPerm, &numRows, &numCols, indices, outOffsets, values, PETSC_TRUE, PETSC_TRUE));
8224   PetscCheck(numRows == numCols, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Symmetric matrix transformation produces rectangular dimensions (%" PetscInt_FMT ", %" PetscInt_FMT ")", numRows, numCols);
8225   *numIndices = numRows;
8226   PetscFunctionReturn(PETSC_SUCCESS);
8227 }
8228 
8229 /*@C
8230   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
8231 
8232   Not collective
8233 
8234   Input Parameters:
8235 + dm         - The `DM`
8236 . section    - The `PetscSection` describing the points (a local section)
8237 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8238 . point      - The point defining the closure
8239 - useClPerm  - Use the closure point permutation if available
8240 
8241   Output Parameters:
8242 + numIndices - The number of dof indices in the closure of point with the input sections
8243 . indices    - The dof indices
8244 . outOffsets - Array to write the field offsets into, or `NULL`
8245 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8246 
8247   Level: advanced
8248 
8249   Notes:
8250   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
8251 
8252   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8253   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
8254   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8255   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
8256   indices (with the above semantics) are implied.
8257 
8258 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
8259 @*/
8260 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8261 {
8262   PetscFunctionBegin;
8263   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8264   PetscAssertPointer(indices, 7);
8265   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
8266   PetscFunctionReturn(PETSC_SUCCESS);
8267 }
8268 
8269 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8270 {
8271   DM_Plex           *mesh = (DM_Plex *)dm->data;
8272   PetscInt          *indices;
8273   PetscInt           numIndices;
8274   const PetscScalar *valuesOrig = values;
8275   PetscErrorCode     ierr;
8276 
8277   PetscFunctionBegin;
8278   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8279   if (!section) PetscCall(DMGetLocalSection(dm, &section));
8280   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8281   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
8282   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
8283   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
8284 
8285   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
8286 
8287   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
8288   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8289   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
8290   if (ierr) {
8291     PetscMPIInt rank;
8292 
8293     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8294     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8295     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
8296     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8297     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8298     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
8299   }
8300   if (mesh->printFEM > 1) {
8301     PetscInt i;
8302     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
8303     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
8304     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8305   }
8306 
8307   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8308   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8309   PetscFunctionReturn(PETSC_SUCCESS);
8310 }
8311 
8312 /*@C
8313   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
8314 
8315   Not collective
8316 
8317   Input Parameters:
8318 + dm            - The `DM`
8319 . section       - The section describing the layout in `v`, or `NULL` to use the default section
8320 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
8321 . A             - The matrix
8322 . point         - The point in the `DM`
8323 . values        - The array of values
8324 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8325 
8326   Level: intermediate
8327 
8328 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8329 @*/
8330 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8331 {
8332   PetscFunctionBegin;
8333   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
8334   PetscFunctionReturn(PETSC_SUCCESS);
8335 }
8336 
8337 /*@C
8338   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
8339 
8340   Not collective
8341 
8342   Input Parameters:
8343 + dmRow            - The `DM` for the row fields
8344 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
8345 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
8346 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
8347 . dmCol            - The `DM` for the column fields
8348 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
8349 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
8350 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
8351 . A                - The matrix
8352 . point            - The point in the `DM`
8353 . values           - The array of values
8354 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8355 
8356   Level: intermediate
8357 
8358 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8359 @*/
8360 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)
8361 {
8362   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
8363   PetscInt          *indicesRow, *indicesCol;
8364   PetscInt           numIndicesRow = -1, numIndicesCol = -1;
8365   const PetscScalar *valuesV0 = values, *valuesV1, *valuesV2;
8366 
8367   PetscErrorCode ierr;
8368 
8369   PetscFunctionBegin;
8370   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
8371   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
8372   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
8373   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
8374   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
8375   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
8376   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
8377   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
8378   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
8379   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
8380   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
8381 
8382   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmRow, sectionRow, point, &numIndicesRow));
8383   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmCol, sectionCol, point, &numIndicesCol));
8384   valuesV1 = valuesV0;
8385   PetscCall(DMPlexGetClosureIndices_Internal(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV1, PETSC_FALSE, PETSC_TRUE));
8386   valuesV2 = valuesV1;
8387   PetscCall(DMPlexGetClosureIndices_Internal(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesRow, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2, PETSC_TRUE, PETSC_FALSE));
8388 
8389   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2));
8390   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8391   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2, mode);
8392   if (ierr) {
8393     PetscMPIInt rank;
8394 
8395     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8396     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8397     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8398     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV2));
8399     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8400     if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8401     if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8402   }
8403 
8404   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2));
8405   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8406   if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8407   if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8408   PetscFunctionReturn(PETSC_SUCCESS);
8409 }
8410 
8411 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8412 {
8413   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8414   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8415   PetscInt       *cpoints = NULL;
8416   PetscInt       *findices, *cindices;
8417   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8418   PetscInt        foffsets[32], coffsets[32];
8419   DMPolytopeType  ct;
8420   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8421   PetscErrorCode  ierr;
8422 
8423   PetscFunctionBegin;
8424   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8425   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8426   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8427   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8428   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8429   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8430   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8431   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8432   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8433   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8434   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8435   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8436   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8437   PetscCall(PetscArrayzero(foffsets, 32));
8438   PetscCall(PetscArrayzero(coffsets, 32));
8439   /* Column indices */
8440   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8441   maxFPoints = numCPoints;
8442   /* Compress out points not in the section */
8443   /*   TODO: Squeeze out points with 0 dof as well */
8444   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8445   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8446     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8447       cpoints[q * 2]     = cpoints[p];
8448       cpoints[q * 2 + 1] = cpoints[p + 1];
8449       ++q;
8450     }
8451   }
8452   numCPoints = q;
8453   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8454     PetscInt fdof;
8455 
8456     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8457     if (!dof) continue;
8458     for (f = 0; f < numFields; ++f) {
8459       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8460       coffsets[f + 1] += fdof;
8461     }
8462     numCIndices += dof;
8463   }
8464   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8465   /* Row indices */
8466   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8467   {
8468     DMPlexTransform tr;
8469     DMPolytopeType *rct;
8470     PetscInt       *rsize, *rcone, *rornt, Nt;
8471 
8472     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8473     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8474     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8475     numSubcells = rsize[Nt - 1];
8476     PetscCall(DMPlexTransformDestroy(&tr));
8477   }
8478   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8479   for (r = 0, q = 0; r < numSubcells; ++r) {
8480     /* TODO Map from coarse to fine cells */
8481     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8482     /* Compress out points not in the section */
8483     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8484     for (p = 0; p < numFPoints * 2; p += 2) {
8485       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8486         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8487         if (!dof) continue;
8488         for (s = 0; s < q; ++s)
8489           if (fpoints[p] == ftotpoints[s * 2]) break;
8490         if (s < q) continue;
8491         ftotpoints[q * 2]     = fpoints[p];
8492         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8493         ++q;
8494       }
8495     }
8496     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8497   }
8498   numFPoints = q;
8499   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8500     PetscInt fdof;
8501 
8502     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8503     if (!dof) continue;
8504     for (f = 0; f < numFields; ++f) {
8505       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8506       foffsets[f + 1] += fdof;
8507     }
8508     numFIndices += dof;
8509   }
8510   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8511 
8512   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8513   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8514   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8515   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8516   if (numFields) {
8517     const PetscInt **permsF[32] = {NULL};
8518     const PetscInt **permsC[32] = {NULL};
8519 
8520     for (f = 0; f < numFields; f++) {
8521       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8522       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8523     }
8524     for (p = 0; p < numFPoints; p++) {
8525       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8526       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8527     }
8528     for (p = 0; p < numCPoints; p++) {
8529       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8530       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8531     }
8532     for (f = 0; f < numFields; f++) {
8533       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8534       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8535     }
8536   } else {
8537     const PetscInt **permsF = NULL;
8538     const PetscInt **permsC = NULL;
8539 
8540     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8541     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8542     for (p = 0, off = 0; p < numFPoints; p++) {
8543       const PetscInt *perm = permsF ? permsF[p] : NULL;
8544 
8545       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8546       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8547     }
8548     for (p = 0, off = 0; p < numCPoints; p++) {
8549       const PetscInt *perm = permsC ? permsC[p] : NULL;
8550 
8551       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8552       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8553     }
8554     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8555     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8556   }
8557   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8558   /* TODO: flips */
8559   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8560   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8561   if (ierr) {
8562     PetscMPIInt rank;
8563 
8564     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8565     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8566     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8567     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8568     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8569   }
8570   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8571   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8572   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8573   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8574   PetscFunctionReturn(PETSC_SUCCESS);
8575 }
8576 
8577 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8578 {
8579   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8580   PetscInt       *cpoints      = NULL;
8581   PetscInt        foffsets[32] = {0}, coffsets[32] = {0};
8582   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8583   DMPolytopeType  ct;
8584   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8585 
8586   PetscFunctionBegin;
8587   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8588   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8589   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8590   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8591   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8592   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8593   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8594   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8595   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8596   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8597   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8598   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8599   /* Column indices */
8600   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8601   maxFPoints = numCPoints;
8602   /* Compress out points not in the section */
8603   /*   TODO: Squeeze out points with 0 dof as well */
8604   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8605   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8606     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8607       cpoints[q * 2]     = cpoints[p];
8608       cpoints[q * 2 + 1] = cpoints[p + 1];
8609       ++q;
8610     }
8611   }
8612   numCPoints = q;
8613   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8614     PetscInt fdof;
8615 
8616     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8617     if (!dof) continue;
8618     for (f = 0; f < numFields; ++f) {
8619       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8620       coffsets[f + 1] += fdof;
8621     }
8622     numCIndices += dof;
8623   }
8624   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8625   /* Row indices */
8626   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8627   {
8628     DMPlexTransform tr;
8629     DMPolytopeType *rct;
8630     PetscInt       *rsize, *rcone, *rornt, Nt;
8631 
8632     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8633     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8634     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8635     numSubcells = rsize[Nt - 1];
8636     PetscCall(DMPlexTransformDestroy(&tr));
8637   }
8638   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8639   for (r = 0, q = 0; r < numSubcells; ++r) {
8640     /* TODO Map from coarse to fine cells */
8641     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8642     /* Compress out points not in the section */
8643     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8644     for (p = 0; p < numFPoints * 2; p += 2) {
8645       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8646         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8647         if (!dof) continue;
8648         for (s = 0; s < q; ++s)
8649           if (fpoints[p] == ftotpoints[s * 2]) break;
8650         if (s < q) continue;
8651         ftotpoints[q * 2]     = fpoints[p];
8652         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8653         ++q;
8654       }
8655     }
8656     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8657   }
8658   numFPoints = q;
8659   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8660     PetscInt fdof;
8661 
8662     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8663     if (!dof) continue;
8664     for (f = 0; f < numFields; ++f) {
8665       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8666       foffsets[f + 1] += fdof;
8667     }
8668     numFIndices += dof;
8669   }
8670   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8671 
8672   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8673   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8674   if (numFields) {
8675     const PetscInt **permsF[32] = {NULL};
8676     const PetscInt **permsC[32] = {NULL};
8677 
8678     for (f = 0; f < numFields; f++) {
8679       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8680       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8681     }
8682     for (p = 0; p < numFPoints; p++) {
8683       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8684       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8685     }
8686     for (p = 0; p < numCPoints; p++) {
8687       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8688       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8689     }
8690     for (f = 0; f < numFields; f++) {
8691       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8692       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8693     }
8694   } else {
8695     const PetscInt **permsF = NULL;
8696     const PetscInt **permsC = NULL;
8697 
8698     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8699     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8700     for (p = 0, off = 0; p < numFPoints; p++) {
8701       const PetscInt *perm = permsF ? permsF[p] : NULL;
8702 
8703       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8704       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8705     }
8706     for (p = 0, off = 0; p < numCPoints; p++) {
8707       const PetscInt *perm = permsC ? permsC[p] : NULL;
8708 
8709       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8710       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8711     }
8712     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8713     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8714   }
8715   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8716   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8717   PetscFunctionReturn(PETSC_SUCCESS);
8718 }
8719 
8720 /*@
8721   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8722 
8723   Input Parameter:
8724 . dm - The `DMPLEX` object
8725 
8726   Output Parameter:
8727 . cellHeight - The height of a cell
8728 
8729   Level: developer
8730 
8731 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8732 @*/
8733 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8734 {
8735   DM_Plex *mesh = (DM_Plex *)dm->data;
8736 
8737   PetscFunctionBegin;
8738   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8739   PetscAssertPointer(cellHeight, 2);
8740   *cellHeight = mesh->vtkCellHeight;
8741   PetscFunctionReturn(PETSC_SUCCESS);
8742 }
8743 
8744 /*@
8745   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8746 
8747   Input Parameters:
8748 + dm         - The `DMPLEX` object
8749 - cellHeight - The height of a cell
8750 
8751   Level: developer
8752 
8753 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8754 @*/
8755 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8756 {
8757   DM_Plex *mesh = (DM_Plex *)dm->data;
8758 
8759   PetscFunctionBegin;
8760   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8761   mesh->vtkCellHeight = cellHeight;
8762   PetscFunctionReturn(PETSC_SUCCESS);
8763 }
8764 
8765 /*@
8766   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8767 
8768   Input Parameters:
8769 + dm - The `DMPLEX` object
8770 - ct - The `DMPolytopeType` of the cell
8771 
8772   Output Parameters:
8773 + start - The first cell of this type, or `NULL`
8774 - end   - The upper bound on this celltype, or `NULL`
8775 
8776   Level: advanced
8777 
8778 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8779 @*/
8780 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PetscInt *start, PetscInt *end)
8781 {
8782   DM_Plex *mesh = (DM_Plex *)dm->data;
8783   DMLabel  label;
8784   PetscInt pStart, pEnd;
8785 
8786   PetscFunctionBegin;
8787   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8788   if (start) {
8789     PetscAssertPointer(start, 3);
8790     *start = 0;
8791   }
8792   if (end) {
8793     PetscAssertPointer(end, 4);
8794     *end = 0;
8795   }
8796   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8797   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8798   if (mesh->tr) {
8799     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8800   } else {
8801     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8802     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8803     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8804   }
8805   PetscFunctionReturn(PETSC_SUCCESS);
8806 }
8807 
8808 /*@
8809   DMPlexGetDepthStratumGlobalSize - Get the global size for a given depth stratum
8810 
8811   Input Parameters:
8812 + dm    - The `DMPLEX` object
8813 - depth - The depth for the given point stratum
8814 
8815   Output Parameter:
8816 . gsize - The global number of points in the stratum
8817 
8818   Level: advanced
8819 
8820 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8821 @*/
8822 PetscErrorCode DMPlexGetDepthStratumGlobalSize(DM dm, PetscInt depth, PetscInt *gsize)
8823 {
8824   PetscSF         sf;
8825   const PetscInt *leaves;
8826   PetscInt        Nl, loc, start, end, lsize = 0;
8827 
8828   PetscFunctionBegin;
8829   PetscCall(DMGetPointSF(dm, &sf));
8830   PetscCall(PetscSFGetGraph(sf, NULL, &Nl, &leaves, NULL));
8831   PetscCall(DMPlexGetDepthStratum(dm, depth, &start, &end));
8832   for (PetscInt p = start; p < end; ++p) {
8833     PetscCall(PetscFindInt(p, Nl, leaves, &loc));
8834     if (loc < 0) ++lsize;
8835   }
8836   PetscCallMPI(MPI_Allreduce(&lsize, gsize, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
8837   PetscFunctionReturn(PETSC_SUCCESS);
8838 }
8839 
8840 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8841 {
8842   PetscSection section, globalSection;
8843   PetscInt    *numbers, p;
8844 
8845   PetscFunctionBegin;
8846   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8847   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8848   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8849   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8850   PetscCall(PetscSectionSetUp(section));
8851   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &globalSection));
8852   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8853   for (p = pStart; p < pEnd; ++p) {
8854     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8855     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8856     else numbers[p - pStart] += shift;
8857   }
8858   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8859   if (globalSize) {
8860     PetscLayout layout;
8861     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8862     PetscCall(PetscLayoutGetSize(layout, globalSize));
8863     PetscCall(PetscLayoutDestroy(&layout));
8864   }
8865   PetscCall(PetscSectionDestroy(&section));
8866   PetscCall(PetscSectionDestroy(&globalSection));
8867   PetscFunctionReturn(PETSC_SUCCESS);
8868 }
8869 
8870 /*@
8871   DMPlexCreateCellNumbering - Get a global cell numbering for all cells on this process
8872 
8873   Input Parameters:
8874 + dm         - The `DMPLEX` object
8875 - includeAll - Whether to include all cells, or just the simplex and box cells
8876 
8877   Output Parameter:
8878 . globalCellNumbers - Global cell numbers for all cells on this process
8879 
8880   Level: developer
8881 
8882 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`
8883 @*/
8884 PetscErrorCode DMPlexCreateCellNumbering(DM dm, PetscBool includeAll, IS *globalCellNumbers)
8885 {
8886   PetscInt cellHeight, cStart, cEnd;
8887 
8888   PetscFunctionBegin;
8889   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8890   if (includeAll) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8891   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8892   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8893   PetscFunctionReturn(PETSC_SUCCESS);
8894 }
8895 
8896 /*@
8897   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8898 
8899   Input Parameter:
8900 . dm - The `DMPLEX` object
8901 
8902   Output Parameter:
8903 . globalCellNumbers - Global cell numbers for all cells on this process
8904 
8905   Level: developer
8906 
8907 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateCellNumbering()`, `DMPlexGetVertexNumbering()`
8908 @*/
8909 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8910 {
8911   DM_Plex *mesh = (DM_Plex *)dm->data;
8912 
8913   PetscFunctionBegin;
8914   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8915   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8916   *globalCellNumbers = mesh->globalCellNumbers;
8917   PetscFunctionReturn(PETSC_SUCCESS);
8918 }
8919 
8920 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8921 {
8922   PetscInt vStart, vEnd;
8923 
8924   PetscFunctionBegin;
8925   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8926   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8927   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8928   PetscFunctionReturn(PETSC_SUCCESS);
8929 }
8930 
8931 /*@
8932   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8933 
8934   Input Parameter:
8935 . dm - The `DMPLEX` object
8936 
8937   Output Parameter:
8938 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8939 
8940   Level: developer
8941 
8942 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8943 @*/
8944 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8945 {
8946   DM_Plex *mesh = (DM_Plex *)dm->data;
8947 
8948   PetscFunctionBegin;
8949   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8950   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8951   *globalVertexNumbers = mesh->globalVertexNumbers;
8952   PetscFunctionReturn(PETSC_SUCCESS);
8953 }
8954 
8955 /*@
8956   DMPlexCreatePointNumbering - Create a global numbering for all points.
8957 
8958   Collective
8959 
8960   Input Parameter:
8961 . dm - The `DMPLEX` object
8962 
8963   Output Parameter:
8964 . globalPointNumbers - Global numbers for all points on this process
8965 
8966   Level: developer
8967 
8968   Notes:
8969   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8970   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8971   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8972   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8973 
8974   The partitioned mesh is
8975   ```
8976   (2)--0--(3)--1--(4)    (1)--0--(2)
8977   ```
8978   and its global numbering is
8979   ```
8980   (3)--0--(4)--1--(5)--2--(6)
8981   ```
8982   Then the global numbering is provided as
8983   ```
8984   [0] Number of indices in set 5
8985   [0] 0 0
8986   [0] 1 1
8987   [0] 2 3
8988   [0] 3 4
8989   [0] 4 -6
8990   [1] Number of indices in set 3
8991   [1] 0 2
8992   [1] 1 5
8993   [1] 2 6
8994   ```
8995 
8996 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8997 @*/
8998 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8999 {
9000   IS          nums[4];
9001   PetscInt    depths[4], gdepths[4], starts[4];
9002   PetscInt    depth, d, shift = 0;
9003   PetscBool   empty = PETSC_FALSE;
9004   PetscMPIInt idepth;
9005 
9006   PetscFunctionBegin;
9007   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9008   PetscCall(DMPlexGetDepth(dm, &depth));
9009   // For unstratified meshes use dim instead of depth
9010   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
9011   // If any stratum is empty, we must mark all empty
9012   for (d = 0; d <= depth; ++d) {
9013     PetscInt end;
9014 
9015     depths[d] = depth - d;
9016     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
9017     if (!(starts[d] - end)) empty = PETSC_TRUE;
9018   }
9019   if (empty)
9020     for (d = 0; d <= depth; ++d) {
9021       depths[d] = -1;
9022       starts[d] = -1;
9023     }
9024   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
9025   PetscCall(PetscMPIIntCast(depth + 1, &idepth));
9026   PetscCallMPI(MPIU_Allreduce(depths, gdepths, idepth, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
9027   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]);
9028   // Note here that 'shift' is collective, so that the numbering is stratified by depth
9029   for (d = 0; d <= depth; ++d) {
9030     PetscInt pStart, pEnd, gsize;
9031 
9032     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
9033     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
9034     shift += gsize;
9035   }
9036   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
9037   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
9038   PetscFunctionReturn(PETSC_SUCCESS);
9039 }
9040 
9041 /*@
9042   DMPlexCreateEdgeNumbering - Create a global numbering for edges.
9043 
9044   Collective
9045 
9046   Input Parameter:
9047 . dm - The `DMPLEX` object
9048 
9049   Output Parameter:
9050 . globalEdgeNumbers - Global numbers for all edges on this process
9051 
9052   Level: developer
9053 
9054   Notes:
9055   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).
9056 
9057 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`, `DMPlexCreatePointNumbering()`
9058 @*/
9059 PetscErrorCode DMPlexCreateEdgeNumbering(DM dm, IS *globalEdgeNumbers)
9060 {
9061   PetscSF  sf;
9062   PetscInt eStart, eEnd;
9063 
9064   PetscFunctionBegin;
9065   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9066   PetscCall(DMGetPointSF(dm, &sf));
9067   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9068   PetscCall(DMPlexCreateNumbering_Plex(dm, eStart, eEnd, 0, NULL, sf, globalEdgeNumbers));
9069   PetscFunctionReturn(PETSC_SUCCESS);
9070 }
9071 
9072 /*@
9073   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
9074 
9075   Input Parameter:
9076 . dm - The `DMPLEX` object
9077 
9078   Output Parameter:
9079 . ranks - The rank field
9080 
9081   Options Database Key:
9082 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
9083 
9084   Level: intermediate
9085 
9086 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
9087 @*/
9088 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
9089 {
9090   DM             rdm;
9091   PetscFE        fe;
9092   PetscScalar   *r;
9093   PetscMPIInt    rank;
9094   DMPolytopeType ct;
9095   PetscInt       dim, cStart, cEnd, c;
9096   PetscBool      simplex;
9097 
9098   PetscFunctionBeginUser;
9099   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9100   PetscAssertPointer(ranks, 2);
9101   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
9102   PetscCall(DMClone(dm, &rdm));
9103   PetscCall(DMGetDimension(rdm, &dim));
9104   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
9105   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
9106   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
9107   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
9108   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
9109   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9110   PetscCall(PetscFEDestroy(&fe));
9111   PetscCall(DMCreateDS(rdm));
9112   PetscCall(DMCreateGlobalVector(rdm, ranks));
9113   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
9114   PetscCall(VecGetArray(*ranks, &r));
9115   for (c = cStart; c < cEnd; ++c) {
9116     PetscScalar *lr;
9117 
9118     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
9119     if (lr) *lr = rank;
9120   }
9121   PetscCall(VecRestoreArray(*ranks, &r));
9122   PetscCall(DMDestroy(&rdm));
9123   PetscFunctionReturn(PETSC_SUCCESS);
9124 }
9125 
9126 /*@
9127   DMPlexCreateLabelField - Create a field whose value is the label value for that point
9128 
9129   Input Parameters:
9130 + dm    - The `DMPLEX`
9131 - label - The `DMLabel`
9132 
9133   Output Parameter:
9134 . val - The label value field
9135 
9136   Options Database Key:
9137 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
9138 
9139   Level: intermediate
9140 
9141 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
9142 @*/
9143 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
9144 {
9145   DM             rdm, plex;
9146   Vec            lval;
9147   PetscSection   section;
9148   PetscFE        fe;
9149   PetscScalar   *v;
9150   PetscInt       dim, pStart, pEnd, p, cStart;
9151   DMPolytopeType ct;
9152   char           name[PETSC_MAX_PATH_LEN];
9153   const char    *lname, *prefix;
9154 
9155   PetscFunctionBeginUser;
9156   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9157   PetscAssertPointer(label, 2);
9158   PetscAssertPointer(val, 3);
9159   PetscCall(DMClone(dm, &rdm));
9160   PetscCall(DMConvert(rdm, DMPLEX, &plex));
9161   PetscCall(DMPlexGetHeightStratum(plex, 0, &cStart, NULL));
9162   PetscCall(DMPlexGetCellType(plex, cStart, &ct));
9163   PetscCall(DMDestroy(&plex));
9164   PetscCall(DMGetDimension(rdm, &dim));
9165   PetscCall(DMGetOptionsPrefix(dm, &prefix));
9166   PetscCall(PetscObjectGetName((PetscObject)label, &lname));
9167   PetscCall(PetscSNPrintf(name, sizeof(name), "%s%s_", prefix ? prefix : "", lname));
9168   PetscCall(PetscFECreateByCell(PETSC_COMM_SELF, dim, 1, ct, name, -1, &fe));
9169   PetscCall(PetscObjectSetName((PetscObject)fe, ""));
9170   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9171   PetscCall(PetscFEDestroy(&fe));
9172   PetscCall(DMCreateDS(rdm));
9173   PetscCall(DMCreateGlobalVector(rdm, val));
9174   PetscCall(DMCreateLocalVector(rdm, &lval));
9175   PetscCall(PetscObjectSetName((PetscObject)*val, lname));
9176   PetscCall(DMGetLocalSection(rdm, &section));
9177   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
9178   PetscCall(VecGetArray(lval, &v));
9179   for (p = pStart; p < pEnd; ++p) {
9180     PetscInt cval, dof, off;
9181 
9182     PetscCall(PetscSectionGetDof(section, p, &dof));
9183     if (!dof) continue;
9184     PetscCall(DMLabelGetValue(label, p, &cval));
9185     PetscCall(PetscSectionGetOffset(section, p, &off));
9186     for (PetscInt d = 0; d < dof; d++) v[off + d] = cval;
9187   }
9188   PetscCall(VecRestoreArray(lval, &v));
9189   PetscCall(DMLocalToGlobal(rdm, lval, INSERT_VALUES, *val));
9190   PetscCall(VecDestroy(&lval));
9191   PetscCall(DMDestroy(&rdm));
9192   PetscFunctionReturn(PETSC_SUCCESS);
9193 }
9194 
9195 /*@
9196   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
9197 
9198   Input Parameter:
9199 . dm - The `DMPLEX` object
9200 
9201   Level: developer
9202 
9203   Notes:
9204   This is a useful diagnostic when creating meshes programmatically.
9205 
9206   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9207 
9208 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9209 @*/
9210 PetscErrorCode DMPlexCheckSymmetry(DM dm)
9211 {
9212   PetscSection    coneSection, supportSection;
9213   const PetscInt *cone, *support;
9214   PetscInt        coneSize, c, supportSize, s;
9215   PetscInt        pStart, pEnd, p, pp, csize, ssize;
9216   PetscBool       storagecheck = PETSC_TRUE;
9217 
9218   PetscFunctionBegin;
9219   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9220   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
9221   PetscCall(DMPlexGetConeSection(dm, &coneSection));
9222   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
9223   /* Check that point p is found in the support of its cone points, and vice versa */
9224   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9225   for (p = pStart; p < pEnd; ++p) {
9226     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
9227     PetscCall(DMPlexGetCone(dm, p, &cone));
9228     for (c = 0; c < coneSize; ++c) {
9229       PetscBool dup = PETSC_FALSE;
9230       PetscInt  d;
9231       for (d = c - 1; d >= 0; --d) {
9232         if (cone[c] == cone[d]) {
9233           dup = PETSC_TRUE;
9234           break;
9235         }
9236       }
9237       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
9238       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
9239       for (s = 0; s < supportSize; ++s) {
9240         if (support[s] == p) break;
9241       }
9242       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
9243         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
9244         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
9245         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9246         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
9247         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
9248         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9249         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]);
9250         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
9251       }
9252     }
9253     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
9254     if (p != pp) {
9255       storagecheck = PETSC_FALSE;
9256       continue;
9257     }
9258     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
9259     PetscCall(DMPlexGetSupport(dm, p, &support));
9260     for (s = 0; s < supportSize; ++s) {
9261       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
9262       PetscCall(DMPlexGetCone(dm, support[s], &cone));
9263       for (c = 0; c < coneSize; ++c) {
9264         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
9265         if (cone[c] != pp) {
9266           c = 0;
9267           break;
9268         }
9269         if (cone[c] == p) break;
9270       }
9271       if (c >= coneSize) {
9272         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
9273         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
9274         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9275         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
9276         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
9277         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9278         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
9279       }
9280     }
9281   }
9282   if (storagecheck) {
9283     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
9284     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
9285     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
9286   }
9287   PetscFunctionReturn(PETSC_SUCCESS);
9288 }
9289 
9290 /*
9291   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.
9292 */
9293 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
9294 {
9295   DMPolytopeType  cct;
9296   PetscInt        ptpoints[4];
9297   const PetscInt *cone, *ccone, *ptcone;
9298   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
9299 
9300   PetscFunctionBegin;
9301   *unsplit = 0;
9302   switch (ct) {
9303   case DM_POLYTOPE_POINT_PRISM_TENSOR:
9304     ptpoints[npt++] = c;
9305     break;
9306   case DM_POLYTOPE_SEG_PRISM_TENSOR:
9307     PetscCall(DMPlexGetCone(dm, c, &cone));
9308     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9309     for (cp = 0; cp < coneSize; ++cp) {
9310       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
9311       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
9312     }
9313     break;
9314   case DM_POLYTOPE_TRI_PRISM_TENSOR:
9315   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9316     PetscCall(DMPlexGetCone(dm, c, &cone));
9317     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9318     for (cp = 0; cp < coneSize; ++cp) {
9319       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
9320       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
9321       for (ccp = 0; ccp < cconeSize; ++ccp) {
9322         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
9323         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
9324           PetscInt p;
9325           for (p = 0; p < npt; ++p)
9326             if (ptpoints[p] == ccone[ccp]) break;
9327           if (p == npt) ptpoints[npt++] = ccone[ccp];
9328         }
9329       }
9330     }
9331     break;
9332   default:
9333     break;
9334   }
9335   for (pt = 0; pt < npt; ++pt) {
9336     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
9337     if (ptcone[0] == ptcone[1]) ++(*unsplit);
9338   }
9339   PetscFunctionReturn(PETSC_SUCCESS);
9340 }
9341 
9342 /*@
9343   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
9344 
9345   Input Parameters:
9346 + dm         - The `DMPLEX` object
9347 - cellHeight - Normally 0
9348 
9349   Level: developer
9350 
9351   Notes:
9352   This is a useful diagnostic when creating meshes programmatically.
9353   Currently applicable only to homogeneous simplex or tensor meshes.
9354 
9355   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9356 
9357 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9358 @*/
9359 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
9360 {
9361   DMPlexInterpolatedFlag interp;
9362   DMPolytopeType         ct;
9363   PetscInt               vStart, vEnd, cStart, cEnd, c;
9364 
9365   PetscFunctionBegin;
9366   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9367   PetscCall(DMPlexIsInterpolated(dm, &interp));
9368   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9369   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9370   for (c = cStart; c < cEnd; ++c) {
9371     PetscInt *closure = NULL;
9372     PetscInt  coneSize, closureSize, cl, Nv = 0;
9373 
9374     PetscCall(DMPlexGetCellType(dm, c, &ct));
9375     if (ct == DM_POLYTOPE_UNKNOWN) continue;
9376     if (interp == DMPLEX_INTERPOLATED_FULL) {
9377       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9378       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));
9379     }
9380     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9381     for (cl = 0; cl < closureSize * 2; cl += 2) {
9382       const PetscInt p = closure[cl];
9383       if ((p >= vStart) && (p < vEnd)) ++Nv;
9384     }
9385     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9386     /* Special Case: Tensor faces with identified vertices */
9387     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
9388       PetscInt unsplit;
9389 
9390       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9391       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
9392     }
9393     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));
9394   }
9395   PetscFunctionReturn(PETSC_SUCCESS);
9396 }
9397 
9398 /*@
9399   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
9400 
9401   Collective
9402 
9403   Input Parameters:
9404 + dm         - The `DMPLEX` object
9405 - cellHeight - Normally 0
9406 
9407   Level: developer
9408 
9409   Notes:
9410   This is a useful diagnostic when creating meshes programmatically.
9411   This routine is only relevant for meshes that are fully interpolated across all ranks.
9412   It will error out if a partially interpolated mesh is given on some rank.
9413   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
9414 
9415   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9416 
9417 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
9418 @*/
9419 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
9420 {
9421   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
9422   DMPlexInterpolatedFlag interpEnum;
9423 
9424   PetscFunctionBegin;
9425   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9426   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
9427   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
9428   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
9429     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
9430     PetscFunctionReturn(PETSC_SUCCESS);
9431   }
9432 
9433   PetscCall(DMGetDimension(dm, &dim));
9434   PetscCall(DMPlexGetDepth(dm, &depth));
9435   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9436   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
9437     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
9438     for (c = cStart; c < cEnd; ++c) {
9439       const PetscInt       *cone, *ornt, *faceSizes, *faces;
9440       const DMPolytopeType *faceTypes;
9441       DMPolytopeType        ct;
9442       PetscInt              numFaces, coneSize, f;
9443       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
9444 
9445       PetscCall(DMPlexGetCellType(dm, c, &ct));
9446       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9447       if (unsplit) continue;
9448       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9449       PetscCall(DMPlexGetCone(dm, c, &cone));
9450       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
9451       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9452       for (cl = 0; cl < closureSize * 2; cl += 2) {
9453         const PetscInt p = closure[cl];
9454         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
9455       }
9456       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9457       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);
9458       for (f = 0; f < numFaces; ++f) {
9459         DMPolytopeType fct;
9460         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
9461 
9462         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
9463         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
9464         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
9465           const PetscInt p = fclosure[cl];
9466           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
9467         }
9468         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]);
9469         for (v = 0; v < fnumCorners; ++v) {
9470           if (fclosure[v] != faces[fOff + v]) {
9471             PetscInt v1;
9472 
9473             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
9474             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
9475             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
9476             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
9477             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9478             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]);
9479           }
9480         }
9481         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
9482         fOff += faceSizes[f];
9483       }
9484       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9485       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9486     }
9487   }
9488   PetscFunctionReturn(PETSC_SUCCESS);
9489 }
9490 
9491 /*@
9492   DMPlexCheckGeometry - Check the geometry of mesh cells
9493 
9494   Input Parameter:
9495 . dm - The `DMPLEX` object
9496 
9497   Level: developer
9498 
9499   Notes:
9500   This is a useful diagnostic when creating meshes programmatically.
9501 
9502   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9503 
9504 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9505 @*/
9506 PetscErrorCode DMPlexCheckGeometry(DM dm)
9507 {
9508   Vec       coordinates;
9509   PetscReal detJ, J[9], refVol = 1.0;
9510   PetscReal vol;
9511   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9512 
9513   PetscFunctionBegin;
9514   PetscCall(DMGetDimension(dm, &dim));
9515   PetscCall(DMGetCoordinateDim(dm, &dE));
9516   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9517   PetscCall(DMPlexGetDepth(dm, &depth));
9518   for (d = 0; d < dim; ++d) refVol *= 2.0;
9519   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9520   /* Make sure local coordinates are created, because that step is collective */
9521   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9522   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9523   for (c = cStart; c < cEnd; ++c) {
9524     DMPolytopeType ct;
9525     PetscInt       unsplit;
9526     PetscBool      ignoreZeroVol = PETSC_FALSE;
9527 
9528     PetscCall(DMPlexGetCellType(dm, c, &ct));
9529     switch (ct) {
9530     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9531     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9532     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9533       ignoreZeroVol = PETSC_TRUE;
9534       break;
9535     default:
9536       break;
9537     }
9538     switch (ct) {
9539     case DM_POLYTOPE_TRI_PRISM:
9540     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9541     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9542     case DM_POLYTOPE_PYRAMID:
9543       continue;
9544     default:
9545       break;
9546     }
9547     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9548     if (unsplit) continue;
9549     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9550     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);
9551     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9552     /* This should work with periodicity since DG coordinates should be used */
9553     if (depth > 1) {
9554       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9555       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);
9556       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9557     }
9558   }
9559   PetscFunctionReturn(PETSC_SUCCESS);
9560 }
9561 
9562 /*@
9563   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9564 
9565   Collective
9566 
9567   Input Parameters:
9568 + dm              - The `DMPLEX` object
9569 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9570 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9571 
9572   Level: developer
9573 
9574   Notes:
9575   This is mainly intended for debugging/testing purposes.
9576 
9577   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9578 
9579   Extra roots can come from periodic cuts, where additional points appear on the boundary
9580 
9581 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9582 @*/
9583 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9584 {
9585   PetscInt           l, nleaves, nroots, overlap;
9586   const PetscInt    *locals;
9587   const PetscSFNode *remotes;
9588   PetscBool          distributed;
9589   MPI_Comm           comm;
9590   PetscMPIInt        rank;
9591 
9592   PetscFunctionBegin;
9593   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9594   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9595   else pointSF = dm->sf;
9596   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9597   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9598   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9599   {
9600     PetscMPIInt mpiFlag;
9601 
9602     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9603     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9604   }
9605   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9606   PetscCall(DMPlexIsDistributed(dm, &distributed));
9607   if (!distributed) {
9608     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);
9609     PetscFunctionReturn(PETSC_SUCCESS);
9610   }
9611   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);
9612   PetscCall(DMPlexGetOverlap(dm, &overlap));
9613 
9614   /* Check SF graph is compatible with DMPlex chart */
9615   {
9616     PetscInt pStart, pEnd, maxLeaf;
9617 
9618     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9619     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9620     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9621     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9622   }
9623 
9624   /* Check Point SF has no local points referenced */
9625   for (l = 0; l < nleaves; l++) {
9626     PetscMPIInt irank;
9627 
9628     PetscCall(PetscMPIIntCast(remotes[l].rank, &irank));
9629     PetscAssert(irank != rank, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains local point %" PetscInt_FMT " <- (%d,%" PetscInt_FMT ")", locals ? locals[l] : l, irank, remotes[l].index);
9630   }
9631 
9632   /* Check there are no cells in interface */
9633   if (!overlap) {
9634     PetscInt cellHeight, cStart, cEnd;
9635 
9636     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9637     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9638     for (l = 0; l < nleaves; ++l) {
9639       const PetscInt point = locals ? locals[l] : l;
9640 
9641       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9642     }
9643   }
9644 
9645   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9646   {
9647     const PetscInt *rootdegree;
9648 
9649     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9650     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9651     for (l = 0; l < nleaves; ++l) {
9652       const PetscInt  point = locals ? locals[l] : l;
9653       const PetscInt *cone;
9654       PetscInt        coneSize, c, idx;
9655 
9656       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9657       PetscCall(DMPlexGetCone(dm, point, &cone));
9658       for (c = 0; c < coneSize; ++c) {
9659         if (!rootdegree[cone[c]]) {
9660           if (locals) {
9661             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9662           } else {
9663             idx = (cone[c] < nleaves) ? cone[c] : -1;
9664           }
9665           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9666         }
9667       }
9668     }
9669   }
9670   PetscFunctionReturn(PETSC_SUCCESS);
9671 }
9672 
9673 /*@
9674   DMPlexCheckOrphanVertices - Check that no vertices are disconnected from the mesh, unless the mesh only consists of disconnected vertices.
9675 
9676   Collective
9677 
9678   Input Parameter:
9679 . dm - The `DMPLEX` object
9680 
9681   Level: developer
9682 
9683   Notes:
9684   This is mainly intended for debugging/testing purposes.
9685 
9686   Other cell types which are disconnected would be caught by the symmetry and face checks.
9687 
9688   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9689 
9690 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheck()`, `DMSetFromOptions()`
9691 @*/
9692 PetscErrorCode DMPlexCheckOrphanVertices(DM dm)
9693 {
9694   PetscInt pStart, pEnd, vStart, vEnd;
9695 
9696   PetscFunctionBegin;
9697   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9698   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9699   if (pStart == vStart && pEnd == vEnd) PetscFunctionReturn(PETSC_SUCCESS);
9700   for (PetscInt v = vStart; v < vEnd; ++v) {
9701     PetscInt suppSize;
9702 
9703     PetscCall(DMPlexGetSupportSize(dm, v, &suppSize));
9704     PetscCheck(suppSize, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Vertex %" PetscInt_FMT " is disconnected from the mesh", v);
9705   }
9706   PetscFunctionReturn(PETSC_SUCCESS);
9707 }
9708 
9709 /*@
9710   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9711 
9712   Input Parameter:
9713 . dm - The `DMPLEX` object
9714 
9715   Level: developer
9716 
9717   Notes:
9718   This is a useful diagnostic when creating meshes programmatically.
9719 
9720   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9721 
9722   Currently does not include `DMPlexCheckCellShape()`.
9723 
9724 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9725 @*/
9726 PetscErrorCode DMPlexCheck(DM dm)
9727 {
9728   PetscInt cellHeight;
9729 
9730   PetscFunctionBegin;
9731   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9732   PetscCall(DMPlexCheckSymmetry(dm));
9733   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9734   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9735   PetscCall(DMPlexCheckGeometry(dm));
9736   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9737   PetscCall(DMPlexCheckInterfaceCones(dm));
9738   PetscCall(DMPlexCheckOrphanVertices(dm));
9739   PetscFunctionReturn(PETSC_SUCCESS);
9740 }
9741 
9742 typedef struct cell_stats {
9743   PetscReal min, max, sum, squaresum;
9744   PetscInt  count;
9745 } cell_stats_t;
9746 
9747 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9748 {
9749   PetscInt i, N = *len;
9750 
9751   for (i = 0; i < N; i++) {
9752     cell_stats_t *A = (cell_stats_t *)a;
9753     cell_stats_t *B = (cell_stats_t *)b;
9754 
9755     B->min = PetscMin(A->min, B->min);
9756     B->max = PetscMax(A->max, B->max);
9757     B->sum += A->sum;
9758     B->squaresum += A->squaresum;
9759     B->count += A->count;
9760   }
9761 }
9762 
9763 /*@
9764   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9765 
9766   Collective
9767 
9768   Input Parameters:
9769 + dm        - The `DMPLEX` object
9770 . output    - If true, statistics will be displayed on `stdout`
9771 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9772 
9773   Level: developer
9774 
9775   Notes:
9776   This is mainly intended for debugging/testing purposes.
9777 
9778   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9779 
9780 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9781 @*/
9782 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9783 {
9784   DM           dmCoarse;
9785   cell_stats_t stats, globalStats;
9786   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9787   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9788   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9789   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9790   PetscMPIInt  rank, size;
9791 
9792   PetscFunctionBegin;
9793   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9794   stats.min = PETSC_MAX_REAL;
9795   stats.max = PETSC_MIN_REAL;
9796   stats.sum = stats.squaresum = 0.;
9797   stats.count                 = 0;
9798 
9799   PetscCallMPI(MPI_Comm_size(comm, &size));
9800   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9801   PetscCall(DMGetCoordinateDim(dm, &cdim));
9802   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9803   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9804   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9805   for (c = cStart; c < cEnd; c++) {
9806     PetscInt  i;
9807     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9808 
9809     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9810     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9811     for (i = 0; i < PetscSqr(cdim); ++i) {
9812       frobJ += J[i] * J[i];
9813       frobInvJ += invJ[i] * invJ[i];
9814     }
9815     cond2 = frobJ * frobInvJ;
9816     cond  = PetscSqrtReal(cond2);
9817 
9818     stats.min = PetscMin(stats.min, cond);
9819     stats.max = PetscMax(stats.max, cond);
9820     stats.sum += cond;
9821     stats.squaresum += cond2;
9822     stats.count++;
9823     if (output && cond > limit) {
9824       PetscSection coordSection;
9825       Vec          coordsLocal;
9826       PetscScalar *coords = NULL;
9827       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9828 
9829       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9830       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9831       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9832       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9833       for (i = 0; i < Nv / cdim; ++i) {
9834         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9835         for (d = 0; d < cdim; ++d) {
9836           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9837           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9838         }
9839         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9840       }
9841       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9842       for (cl = 0; cl < clSize * 2; cl += 2) {
9843         const PetscInt edge = closure[cl];
9844 
9845         if ((edge >= eStart) && (edge < eEnd)) {
9846           PetscReal len;
9847 
9848           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9849           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9850         }
9851       }
9852       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9853       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9854     }
9855   }
9856   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9857 
9858   if (size > 1) {
9859     PetscMPIInt  blockLengths[2] = {4, 1};
9860     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9861     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9862     MPI_Op       statReduce;
9863 
9864     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9865     PetscCallMPI(MPI_Type_commit(&statType));
9866     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9867     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9868     PetscCallMPI(MPI_Op_free(&statReduce));
9869     PetscCallMPI(MPI_Type_free(&statType));
9870   } else {
9871     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9872   }
9873   if (rank == 0) {
9874     count = globalStats.count;
9875     min   = globalStats.min;
9876     max   = globalStats.max;
9877     mean  = globalStats.sum / globalStats.count;
9878     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9879   }
9880 
9881   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));
9882   PetscCall(PetscFree2(J, invJ));
9883 
9884   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9885   if (dmCoarse) {
9886     PetscBool isplex;
9887 
9888     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9889     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9890   }
9891   PetscFunctionReturn(PETSC_SUCCESS);
9892 }
9893 
9894 /*@
9895   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9896   orthogonal quality below given tolerance.
9897 
9898   Collective
9899 
9900   Input Parameters:
9901 + dm   - The `DMPLEX` object
9902 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9903 - atol - [0, 1] Absolute tolerance for tagging cells.
9904 
9905   Output Parameters:
9906 + OrthQual      - `Vec` containing orthogonal quality per cell
9907 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9908 
9909   Options Database Keys:
9910 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9911 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9912 
9913   Level: intermediate
9914 
9915   Notes:
9916   Orthogonal quality is given by the following formula\:
9917 
9918   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9919 
9920   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
9921   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9922   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9923   calculating the cosine of the angle between these vectors.
9924 
9925   Orthogonal quality ranges from 1 (best) to 0 (worst).
9926 
9927   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9928   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9929 
9930   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9931 
9932 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9933 @*/
9934 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9935 {
9936   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9937   PetscInt              *idx;
9938   PetscScalar           *oqVals;
9939   const PetscScalar     *cellGeomArr, *faceGeomArr;
9940   PetscReal             *ci, *fi, *Ai;
9941   MPI_Comm               comm;
9942   Vec                    cellgeom, facegeom;
9943   DM                     dmFace, dmCell;
9944   IS                     glob;
9945   ISLocalToGlobalMapping ltog;
9946   PetscViewer            vwr;
9947 
9948   PetscFunctionBegin;
9949   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9950   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9951   PetscAssertPointer(OrthQual, 4);
9952   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9953   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9954   PetscCall(DMGetDimension(dm, &nc));
9955   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9956   {
9957     DMPlexInterpolatedFlag interpFlag;
9958 
9959     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9960     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9961       PetscMPIInt rank;
9962 
9963       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9964       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9965     }
9966   }
9967   if (OrthQualLabel) {
9968     PetscAssertPointer(OrthQualLabel, 5);
9969     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9970     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9971   } else {
9972     *OrthQualLabel = NULL;
9973   }
9974   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9975   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9976   PetscCall(DMPlexCreateCellNumbering(dm, PETSC_TRUE, &glob));
9977   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9978   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9979   PetscCall(VecCreate(comm, OrthQual));
9980   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9981   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9982   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9983   PetscCall(VecSetUp(*OrthQual));
9984   PetscCall(ISDestroy(&glob));
9985   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9986   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9987   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9988   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9989   PetscCall(VecGetDM(cellgeom, &dmCell));
9990   PetscCall(VecGetDM(facegeom, &dmFace));
9991   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9992   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9993     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9994     PetscInt         cellarr[2], *adj = NULL;
9995     PetscScalar     *cArr, *fArr;
9996     PetscReal        minvalc = 1.0, minvalf = 1.0;
9997     PetscFVCellGeom *cg;
9998 
9999     idx[cellIter] = cell - cStart;
10000     cellarr[0]    = cell;
10001     /* Make indexing into cellGeom easier */
10002     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
10003     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
10004     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
10005     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
10006     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
10007       PetscInt         i;
10008       const PetscInt   neigh  = adj[cellneigh];
10009       PetscReal        normci = 0, normfi = 0, normai = 0;
10010       PetscFVCellGeom *cgneigh;
10011       PetscFVFaceGeom *fg;
10012 
10013       /* Don't count ourselves in the neighbor list */
10014       if (neigh == cell) continue;
10015       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
10016       cellarr[1] = neigh;
10017       {
10018         PetscInt        numcovpts;
10019         const PetscInt *covpts;
10020 
10021         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
10022         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
10023         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
10024       }
10025 
10026       /* Compute c_i, f_i and their norms */
10027       for (i = 0; i < nc; i++) {
10028         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
10029         fi[i] = fg->centroid[i] - cg->centroid[i];
10030         Ai[i] = fg->normal[i];
10031         normci += PetscPowReal(ci[i], 2);
10032         normfi += PetscPowReal(fi[i], 2);
10033         normai += PetscPowReal(Ai[i], 2);
10034       }
10035       normci = PetscSqrtReal(normci);
10036       normfi = PetscSqrtReal(normfi);
10037       normai = PetscSqrtReal(normai);
10038 
10039       /* Normalize and compute for each face-cell-normal pair */
10040       for (i = 0; i < nc; i++) {
10041         ci[i] = ci[i] / normci;
10042         fi[i] = fi[i] / normfi;
10043         Ai[i] = Ai[i] / normai;
10044         /* PetscAbs because I don't know if normals are guaranteed to point out */
10045         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
10046         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
10047       }
10048       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
10049       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
10050     }
10051     PetscCall(PetscFree(adj));
10052     PetscCall(PetscFree2(cArr, fArr));
10053     /* Defer to cell if they're equal */
10054     oqVals[cellIter] = PetscMin(minvalf, minvalc);
10055     if (OrthQualLabel) {
10056       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
10057     }
10058   }
10059   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
10060   PetscCall(VecAssemblyBegin(*OrthQual));
10061   PetscCall(VecAssemblyEnd(*OrthQual));
10062   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
10063   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
10064   PetscCall(PetscOptionsCreateViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
10065   if (OrthQualLabel) {
10066     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
10067   }
10068   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
10069   PetscCall(PetscViewerDestroy(&vwr));
10070   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
10071   PetscFunctionReturn(PETSC_SUCCESS);
10072 }
10073 
10074 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
10075  * interpolator construction */
10076 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
10077 {
10078   PetscSection section, newSection, gsection;
10079   PetscSF      sf;
10080   PetscBool    hasConstraints, ghasConstraints;
10081 
10082   PetscFunctionBegin;
10083   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10084   PetscAssertPointer(odm, 2);
10085   PetscCall(DMGetLocalSection(dm, &section));
10086   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
10087   PetscCallMPI(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
10088   if (!ghasConstraints) {
10089     PetscCall(PetscObjectReference((PetscObject)dm));
10090     *odm = dm;
10091     PetscFunctionReturn(PETSC_SUCCESS);
10092   }
10093   PetscCall(DMClone(dm, odm));
10094   PetscCall(DMCopyFields(dm, PETSC_DETERMINE, PETSC_DETERMINE, *odm));
10095   PetscCall(DMGetLocalSection(*odm, &newSection));
10096   PetscCall(DMGetPointSF(*odm, &sf));
10097   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_TRUE, PETSC_FALSE, &gsection));
10098   PetscCall(DMSetGlobalSection(*odm, gsection));
10099   PetscCall(PetscSectionDestroy(&gsection));
10100   PetscFunctionReturn(PETSC_SUCCESS);
10101 }
10102 
10103 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
10104 {
10105   DM        dmco, dmfo;
10106   Mat       interpo;
10107   Vec       rscale;
10108   Vec       cglobalo, clocal;
10109   Vec       fglobal, fglobalo, flocal;
10110   PetscBool regular;
10111 
10112   PetscFunctionBegin;
10113   PetscCall(DMGetFullDM(dmc, &dmco));
10114   PetscCall(DMGetFullDM(dmf, &dmfo));
10115   PetscCall(DMSetCoarseDM(dmfo, dmco));
10116   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
10117   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
10118   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
10119   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
10120   PetscCall(DMCreateLocalVector(dmc, &clocal));
10121   PetscCall(VecSet(cglobalo, 0.));
10122   PetscCall(VecSet(clocal, 0.));
10123   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
10124   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
10125   PetscCall(DMCreateLocalVector(dmf, &flocal));
10126   PetscCall(VecSet(fglobal, 0.));
10127   PetscCall(VecSet(fglobalo, 0.));
10128   PetscCall(VecSet(flocal, 0.));
10129   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
10130   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
10131   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
10132   PetscCall(MatMult(interpo, cglobalo, fglobalo));
10133   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
10134   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
10135   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
10136   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
10137   *shift = fglobal;
10138   PetscCall(VecDestroy(&flocal));
10139   PetscCall(VecDestroy(&fglobalo));
10140   PetscCall(VecDestroy(&clocal));
10141   PetscCall(VecDestroy(&cglobalo));
10142   PetscCall(VecDestroy(&rscale));
10143   PetscCall(MatDestroy(&interpo));
10144   PetscCall(DMDestroy(&dmfo));
10145   PetscCall(DMDestroy(&dmco));
10146   PetscFunctionReturn(PETSC_SUCCESS);
10147 }
10148 
10149 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
10150 {
10151   PetscObject shifto;
10152   Vec         shift;
10153 
10154   PetscFunctionBegin;
10155   if (!interp) {
10156     Vec rscale;
10157 
10158     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
10159     PetscCall(VecDestroy(&rscale));
10160   } else {
10161     PetscCall(PetscObjectReference((PetscObject)interp));
10162   }
10163   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
10164   if (!shifto) {
10165     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
10166     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
10167     shifto = (PetscObject)shift;
10168     PetscCall(VecDestroy(&shift));
10169   }
10170   shift = (Vec)shifto;
10171   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
10172   PetscCall(VecAXPY(fineSol, 1.0, shift));
10173   PetscCall(MatDestroy(&interp));
10174   PetscFunctionReturn(PETSC_SUCCESS);
10175 }
10176 
10177 /* Pointwise interpolation
10178      Just code FEM for now
10179      u^f = I u^c
10180      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
10181      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
10182      I_{ij} = psi^f_i phi^c_j
10183 */
10184 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
10185 {
10186   PetscSection gsc, gsf;
10187   PetscInt     m, n;
10188   void        *ctx;
10189   DM           cdm;
10190   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
10191 
10192   PetscFunctionBegin;
10193   PetscCall(DMGetGlobalSection(dmFine, &gsf));
10194   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10195   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10196   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10197 
10198   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
10199   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
10200   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10201   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
10202   PetscCall(DMGetApplicationContext(dmFine, &ctx));
10203 
10204   PetscCall(DMGetCoarseDM(dmFine, &cdm));
10205   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10206   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
10207   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
10208   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
10209   if (scaling) {
10210     /* Use naive scaling */
10211     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
10212   }
10213   PetscFunctionReturn(PETSC_SUCCESS);
10214 }
10215 
10216 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
10217 {
10218   VecScatter ctx;
10219 
10220   PetscFunctionBegin;
10221   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
10222   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
10223   PetscCall(VecScatterDestroy(&ctx));
10224   PetscFunctionReturn(PETSC_SUCCESS);
10225 }
10226 
10227 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[])
10228 {
10229   const PetscInt f  = (PetscInt)PetscRealPart(constants[numConstants]);
10230   const PetscInt Nc = uOff[f + 1] - uOff[f];
10231   for (PetscInt c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
10232 }
10233 
10234 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *lmass, Vec *mass)
10235 {
10236   DM           dmc;
10237   PetscDS      ds;
10238   Vec          ones, locmass;
10239   IS           cellIS;
10240   PetscFormKey key;
10241   PetscInt     depth;
10242 
10243   PetscFunctionBegin;
10244   PetscCall(DMClone(dm, &dmc));
10245   PetscCall(DMCopyDisc(dm, dmc));
10246   PetscCall(DMGetDS(dmc, &ds));
10247   for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10248   if (mass) PetscCall(DMCreateGlobalVector(dm, mass));
10249   if (lmass) PetscCall(DMCreateLocalVector(dm, &locmass));
10250   else PetscCall(DMGetLocalVector(dm, &locmass));
10251   PetscCall(DMGetLocalVector(dm, &ones));
10252   PetscCall(DMPlexGetDepth(dm, &depth));
10253   PetscCall(DMGetStratumIS(dm, "depth", depth, &cellIS));
10254   PetscCall(VecSet(locmass, 0.0));
10255   PetscCall(VecSet(ones, 1.0));
10256   key.label = NULL;
10257   key.value = 0;
10258   key.field = 0;
10259   key.part  = 0;
10260   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
10261   PetscCall(ISDestroy(&cellIS));
10262   if (mass) {
10263     PetscCall(DMLocalToGlobalBegin(dm, locmass, ADD_VALUES, *mass));
10264     PetscCall(DMLocalToGlobalEnd(dm, locmass, ADD_VALUES, *mass));
10265   }
10266   PetscCall(DMRestoreLocalVector(dm, &ones));
10267   if (lmass) *lmass = locmass;
10268   else PetscCall(DMRestoreLocalVector(dm, &locmass));
10269   PetscCall(DMDestroy(&dmc));
10270   PetscFunctionReturn(PETSC_SUCCESS);
10271 }
10272 
10273 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
10274 {
10275   PetscSection gsc, gsf;
10276   PetscInt     m, n;
10277   void        *ctx;
10278   DM           cdm;
10279   PetscBool    regular;
10280 
10281   PetscFunctionBegin;
10282   if (dmFine == dmCoarse) {
10283     DM            dmc;
10284     PetscDS       ds;
10285     PetscWeakForm wf;
10286     Vec           u;
10287     IS            cellIS;
10288     PetscFormKey  key;
10289     PetscInt      depth;
10290 
10291     PetscCall(DMClone(dmFine, &dmc));
10292     PetscCall(DMCopyDisc(dmFine, dmc));
10293     PetscCall(DMGetDS(dmc, &ds));
10294     PetscCall(PetscDSGetWeakForm(ds, &wf));
10295     PetscCall(PetscWeakFormClear(wf));
10296     for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10297     PetscCall(DMCreateMatrix(dmc, mass));
10298     PetscCall(DMGetLocalVector(dmc, &u));
10299     PetscCall(DMPlexGetDepth(dmc, &depth));
10300     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
10301     PetscCall(MatZeroEntries(*mass));
10302     key.label = NULL;
10303     key.value = 0;
10304     key.field = 0;
10305     key.part  = 0;
10306     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
10307     PetscCall(ISDestroy(&cellIS));
10308     PetscCall(DMRestoreLocalVector(dmc, &u));
10309     PetscCall(DMDestroy(&dmc));
10310   } else {
10311     PetscCall(DMGetGlobalSection(dmFine, &gsf));
10312     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10313     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10314     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10315 
10316     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
10317     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10318     PetscCall(MatSetType(*mass, dmCoarse->mattype));
10319     PetscCall(DMGetApplicationContext(dmFine, &ctx));
10320 
10321     PetscCall(DMGetCoarseDM(dmFine, &cdm));
10322     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10323     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
10324     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
10325   }
10326   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
10327   PetscFunctionReturn(PETSC_SUCCESS);
10328 }
10329 
10330 /*@
10331   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10332 
10333   Input Parameter:
10334 . dm - The `DMPLEX` object
10335 
10336   Output Parameter:
10337 . regular - The flag
10338 
10339   Level: intermediate
10340 
10341 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
10342 @*/
10343 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
10344 {
10345   PetscFunctionBegin;
10346   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10347   PetscAssertPointer(regular, 2);
10348   *regular = ((DM_Plex *)dm->data)->regularRefinement;
10349   PetscFunctionReturn(PETSC_SUCCESS);
10350 }
10351 
10352 /*@
10353   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10354 
10355   Input Parameters:
10356 + dm      - The `DMPLEX` object
10357 - regular - The flag
10358 
10359   Level: intermediate
10360 
10361 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
10362 @*/
10363 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
10364 {
10365   PetscFunctionBegin;
10366   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10367   ((DM_Plex *)dm->data)->regularRefinement = regular;
10368   PetscFunctionReturn(PETSC_SUCCESS);
10369 }
10370 
10371 /*@
10372   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
10373   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
10374 
10375   Not Collective
10376 
10377   Input Parameter:
10378 . dm - The `DMPLEX` object
10379 
10380   Output Parameters:
10381 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
10382 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
10383 
10384   Level: intermediate
10385 
10386 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
10387 @*/
10388 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
10389 {
10390   DM_Plex *plex = (DM_Plex *)dm->data;
10391 
10392   PetscFunctionBegin;
10393   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10394   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
10395   if (anchorSection) *anchorSection = plex->anchorSection;
10396   if (anchorIS) *anchorIS = plex->anchorIS;
10397   PetscFunctionReturn(PETSC_SUCCESS);
10398 }
10399 
10400 /*@
10401   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
10402 
10403   Collective
10404 
10405   Input Parameters:
10406 + dm            - The `DMPLEX` object
10407 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
10408                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10409 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10410 
10411   Level: intermediate
10412 
10413   Notes:
10414   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
10415   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
10416   combination of other points' degrees of freedom.
10417 
10418   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
10419   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
10420 
10421   The reference counts of `anchorSection` and `anchorIS` are incremented.
10422 
10423 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
10424 @*/
10425 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
10426 {
10427   DM_Plex    *plex = (DM_Plex *)dm->data;
10428   PetscMPIInt result;
10429 
10430   PetscFunctionBegin;
10431   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10432   if (anchorSection) {
10433     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
10434     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
10435     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
10436   }
10437   if (anchorIS) {
10438     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
10439     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
10440     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
10441   }
10442 
10443   PetscCall(PetscObjectReference((PetscObject)anchorSection));
10444   PetscCall(PetscSectionDestroy(&plex->anchorSection));
10445   plex->anchorSection = anchorSection;
10446 
10447   PetscCall(PetscObjectReference((PetscObject)anchorIS));
10448   PetscCall(ISDestroy(&plex->anchorIS));
10449   plex->anchorIS = anchorIS;
10450 
10451   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
10452     PetscInt        size, a, pStart, pEnd;
10453     const PetscInt *anchors;
10454 
10455     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10456     PetscCall(ISGetLocalSize(anchorIS, &size));
10457     PetscCall(ISGetIndices(anchorIS, &anchors));
10458     for (a = 0; a < size; a++) {
10459       PetscInt p;
10460 
10461       p = anchors[a];
10462       if (p >= pStart && p < pEnd) {
10463         PetscInt dof;
10464 
10465         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10466         if (dof) {
10467           PetscCall(ISRestoreIndices(anchorIS, &anchors));
10468           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
10469         }
10470       }
10471     }
10472     PetscCall(ISRestoreIndices(anchorIS, &anchors));
10473   }
10474   /* reset the generic constraints */
10475   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
10476   PetscFunctionReturn(PETSC_SUCCESS);
10477 }
10478 
10479 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
10480 {
10481   PetscSection anchorSection;
10482   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
10483 
10484   PetscFunctionBegin;
10485   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10486   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10487   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
10488   PetscCall(PetscSectionGetNumFields(section, &numFields));
10489   if (numFields) {
10490     PetscInt f;
10491     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
10492 
10493     for (f = 0; f < numFields; f++) {
10494       PetscInt numComp;
10495 
10496       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
10497       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
10498     }
10499   }
10500   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10501   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10502   pStart = PetscMax(pStart, sStart);
10503   pEnd   = PetscMin(pEnd, sEnd);
10504   pEnd   = PetscMax(pStart, pEnd);
10505   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
10506   for (p = pStart; p < pEnd; p++) {
10507     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10508     if (dof) {
10509       PetscCall(PetscSectionGetDof(section, p, &dof));
10510       PetscCall(PetscSectionSetDof(*cSec, p, dof));
10511       for (f = 0; f < numFields; f++) {
10512         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
10513         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
10514       }
10515     }
10516   }
10517   PetscCall(PetscSectionSetUp(*cSec));
10518   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
10519   PetscFunctionReturn(PETSC_SUCCESS);
10520 }
10521 
10522 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
10523 {
10524   PetscSection    aSec;
10525   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
10526   const PetscInt *anchors;
10527   PetscInt        numFields, f;
10528   IS              aIS;
10529   MatType         mtype;
10530   PetscBool       iscuda, iskokkos;
10531 
10532   PetscFunctionBegin;
10533   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10534   PetscCall(PetscSectionGetStorageSize(cSec, &m));
10535   PetscCall(PetscSectionGetStorageSize(section, &n));
10536   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
10537   PetscCall(MatSetSizes(*cMat, m, n, m, n));
10538   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
10539   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
10540   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
10541   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
10542   if (iscuda) mtype = MATSEQAIJCUSPARSE;
10543   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
10544   else mtype = MATSEQAIJ;
10545   PetscCall(MatSetType(*cMat, mtype));
10546   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
10547   PetscCall(ISGetIndices(aIS, &anchors));
10548   /* cSec will be a subset of aSec and section */
10549   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
10550   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10551   PetscCall(PetscMalloc1(m + 1, &i));
10552   i[0] = 0;
10553   PetscCall(PetscSectionGetNumFields(section, &numFields));
10554   for (p = pStart; p < pEnd; p++) {
10555     PetscInt rDof, rOff, r;
10556 
10557     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10558     if (!rDof) continue;
10559     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10560     if (numFields) {
10561       for (f = 0; f < numFields; f++) {
10562         annz = 0;
10563         for (r = 0; r < rDof; r++) {
10564           a = anchors[rOff + r];
10565           if (a < sStart || a >= sEnd) continue;
10566           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10567           annz += aDof;
10568         }
10569         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10570         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10571         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10572       }
10573     } else {
10574       annz = 0;
10575       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10576       for (q = 0; q < dof; q++) {
10577         a = anchors[rOff + q];
10578         if (a < sStart || a >= sEnd) continue;
10579         PetscCall(PetscSectionGetDof(section, a, &aDof));
10580         annz += aDof;
10581       }
10582       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10583       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10584       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10585     }
10586   }
10587   nnz = i[m];
10588   PetscCall(PetscMalloc1(nnz, &j));
10589   offset = 0;
10590   for (p = pStart; p < pEnd; p++) {
10591     if (numFields) {
10592       for (f = 0; f < numFields; f++) {
10593         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10594         for (q = 0; q < dof; q++) {
10595           PetscInt rDof, rOff, r;
10596           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10597           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10598           for (r = 0; r < rDof; r++) {
10599             PetscInt s;
10600 
10601             a = anchors[rOff + r];
10602             if (a < sStart || a >= sEnd) continue;
10603             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10604             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10605             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10606           }
10607         }
10608       }
10609     } else {
10610       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10611       for (q = 0; q < dof; q++) {
10612         PetscInt rDof, rOff, r;
10613         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10614         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10615         for (r = 0; r < rDof; r++) {
10616           PetscInt s;
10617 
10618           a = anchors[rOff + r];
10619           if (a < sStart || a >= sEnd) continue;
10620           PetscCall(PetscSectionGetDof(section, a, &aDof));
10621           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10622           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10623         }
10624       }
10625     }
10626   }
10627   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10628   PetscCall(PetscFree(i));
10629   PetscCall(PetscFree(j));
10630   PetscCall(ISRestoreIndices(aIS, &anchors));
10631   PetscFunctionReturn(PETSC_SUCCESS);
10632 }
10633 
10634 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10635 {
10636   DM_Plex     *plex = (DM_Plex *)dm->data;
10637   PetscSection anchorSection, section, cSec;
10638   Mat          cMat;
10639 
10640   PetscFunctionBegin;
10641   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10642   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10643   if (anchorSection) {
10644     PetscInt Nf;
10645 
10646     PetscCall(DMGetLocalSection(dm, &section));
10647     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10648     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10649     PetscCall(DMGetNumFields(dm, &Nf));
10650     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10651     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10652     PetscCall(PetscSectionDestroy(&cSec));
10653     PetscCall(MatDestroy(&cMat));
10654   }
10655   PetscFunctionReturn(PETSC_SUCCESS);
10656 }
10657 
10658 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10659 {
10660   IS           subis;
10661   PetscSection section, subsection;
10662 
10663   PetscFunctionBegin;
10664   PetscCall(DMGetLocalSection(dm, &section));
10665   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10666   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10667   /* Create subdomain */
10668   PetscCall(DMPlexFilter(dm, label, value, PETSC_FALSE, PETSC_FALSE, NULL, subdm));
10669   /* Create submodel */
10670   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10671   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10672   PetscCall(DMSetLocalSection(*subdm, subsection));
10673   PetscCall(PetscSectionDestroy(&subsection));
10674   PetscCall(DMCopyDisc(dm, *subdm));
10675   /* Create map from submodel to global model */
10676   if (is) {
10677     PetscSection    sectionGlobal, subsectionGlobal;
10678     IS              spIS;
10679     const PetscInt *spmap;
10680     PetscInt       *subIndices;
10681     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10682     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10683 
10684     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10685     PetscCall(ISGetIndices(spIS, &spmap));
10686     PetscCall(PetscSectionGetNumFields(section, &Nf));
10687     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10688     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10689     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10690     for (p = pStart; p < pEnd; ++p) {
10691       PetscInt gdof, pSubSize = 0;
10692 
10693       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10694       if (gdof > 0) {
10695         for (f = 0; f < Nf; ++f) {
10696           PetscInt fdof, fcdof;
10697 
10698           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10699           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10700           pSubSize += fdof - fcdof;
10701         }
10702         subSize += pSubSize;
10703         if (pSubSize) {
10704           if (bs < 0) {
10705             bs = pSubSize;
10706           } else if (bs != pSubSize) {
10707             /* Layout does not admit a pointwise block size */
10708             bs = 1;
10709           }
10710         }
10711       }
10712     }
10713     /* Must have same blocksize on all procs (some might have no points) */
10714     bsLocal[0] = bs < 0 ? PETSC_INT_MAX : bs;
10715     bsLocal[1] = bs;
10716     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10717     if (bsMinMax[0] != bsMinMax[1]) {
10718       bs = 1;
10719     } else {
10720       bs = bsMinMax[0];
10721     }
10722     PetscCall(PetscMalloc1(subSize, &subIndices));
10723     for (p = pStart; p < pEnd; ++p) {
10724       PetscInt gdof, goff;
10725 
10726       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10727       if (gdof > 0) {
10728         const PetscInt point = spmap[p];
10729 
10730         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10731         for (f = 0; f < Nf; ++f) {
10732           PetscInt fdof, fcdof, fc, f2, poff = 0;
10733 
10734           /* Can get rid of this loop by storing field information in the global section */
10735           for (f2 = 0; f2 < f; ++f2) {
10736             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10737             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10738             poff += fdof - fcdof;
10739           }
10740           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10741           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10742           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10743         }
10744       }
10745     }
10746     PetscCall(ISRestoreIndices(spIS, &spmap));
10747     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10748     if (bs > 1) {
10749       /* We need to check that the block size does not come from non-contiguous fields */
10750       PetscInt i, j, set = 1;
10751       for (i = 0; i < subSize; i += bs) {
10752         for (j = 0; j < bs; ++j) {
10753           if (subIndices[i + j] != subIndices[i] + j) {
10754             set = 0;
10755             break;
10756           }
10757         }
10758       }
10759       if (set) PetscCall(ISSetBlockSize(*is, bs));
10760     }
10761     /* Attach nullspace */
10762     for (f = 0; f < Nf; ++f) {
10763       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10764       if ((*subdm)->nullspaceConstructors[f]) break;
10765     }
10766     if (f < Nf) {
10767       MatNullSpace nullSpace;
10768       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10769 
10770       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10771       PetscCall(MatNullSpaceDestroy(&nullSpace));
10772     }
10773   }
10774   PetscFunctionReturn(PETSC_SUCCESS);
10775 }
10776 
10777 /*@
10778   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10779 
10780   Input Parameters:
10781 + dm    - The `DM`
10782 - dummy - unused argument
10783 
10784   Options Database Key:
10785 . -dm_plex_monitor_throughput - Activate the monitor
10786 
10787   Level: developer
10788 
10789 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10790 @*/
10791 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10792 {
10793   PetscLogHandler default_handler;
10794 
10795   PetscFunctionBegin;
10796   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10797   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10798   if (default_handler) {
10799     PetscLogEvent      event;
10800     PetscEventPerfInfo eventInfo;
10801     PetscLogDouble     cellRate, flopRate;
10802     PetscInt           cStart, cEnd, Nf, N;
10803     const char        *name;
10804 
10805     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10806     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10807     PetscCall(DMGetNumFields(dm, &Nf));
10808     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10809     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10810     N        = (cEnd - cStart) * Nf * eventInfo.count;
10811     flopRate = eventInfo.flops / eventInfo.time;
10812     cellRate = N / eventInfo.time;
10813     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DM (%s) FE Residual Integration: %" PetscInt_FMT " integrals %d reps\n  Cell rate: %.2g/s flop rate: %.2g MF/s\n", name ? name : "unknown", N, eventInfo.count, cellRate, flopRate / 1.e6));
10814   } else {
10815     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.");
10816   }
10817   PetscFunctionReturn(PETSC_SUCCESS);
10818 }
10819