xref: /petsc/src/dm/impls/plex/plex.c (revision 3f52fa0130eb585ec78edcac28423473601b1de5)
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   PetscBool      preferTensor;
4618 
4619   PetscFunctionBeginHot;
4620   PetscCall(DMGetDimension(dm, &dim));
4621   PetscCall(DMPlexGetDepth(dm, &depth));
4622   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4623   PetscCall(DMPlexGetInterpolatePreferTensor(dm, &preferTensor));
4624   pheight = depth - pdepth;
4625   if (depth <= 1) {
4626     switch (pdepth) {
4627     case 0:
4628       ct = DM_POLYTOPE_POINT;
4629       break;
4630     case 1:
4631       switch (coneSize) {
4632       case 2:
4633         ct = DM_POLYTOPE_SEGMENT;
4634         break;
4635       case 3:
4636         ct = DM_POLYTOPE_TRIANGLE;
4637         break;
4638       case 4:
4639         switch (dim) {
4640         case 2:
4641           ct = DM_POLYTOPE_QUADRILATERAL;
4642           break;
4643         case 3:
4644           ct = DM_POLYTOPE_TETRAHEDRON;
4645           break;
4646         default:
4647           break;
4648         }
4649         break;
4650       case 5:
4651         ct = DM_POLYTOPE_PYRAMID;
4652         break;
4653       case 6:
4654         ct = preferTensor ? DM_POLYTOPE_TRI_PRISM_TENSOR : DM_POLYTOPE_TRI_PRISM;
4655         break;
4656       case 8:
4657         ct = DM_POLYTOPE_HEXAHEDRON;
4658         break;
4659       default:
4660         break;
4661       }
4662     }
4663   } else {
4664     if (pdepth == 0) {
4665       ct = DM_POLYTOPE_POINT;
4666     } else if (pheight == 0) {
4667       switch (dim) {
4668       case 1:
4669         switch (coneSize) {
4670         case 2:
4671           ct = DM_POLYTOPE_SEGMENT;
4672           break;
4673         default:
4674           break;
4675         }
4676         break;
4677       case 2:
4678         switch (coneSize) {
4679         case 3:
4680           ct = DM_POLYTOPE_TRIANGLE;
4681           break;
4682         case 4:
4683           ct = DM_POLYTOPE_QUADRILATERAL;
4684           break;
4685         default:
4686           break;
4687         }
4688         break;
4689       case 3:
4690         switch (coneSize) {
4691         case 4:
4692           ct = DM_POLYTOPE_TETRAHEDRON;
4693           break;
4694         case 5: {
4695           const PetscInt *cone;
4696           PetscInt        faceConeSize;
4697 
4698           PetscCall(DMPlexGetCone(dm, p, &cone));
4699           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4700           switch (faceConeSize) {
4701           case 3:
4702             ct = preferTensor ? DM_POLYTOPE_TRI_PRISM_TENSOR : DM_POLYTOPE_TRI_PRISM;
4703             break;
4704           case 4:
4705             ct = DM_POLYTOPE_PYRAMID;
4706             break;
4707           }
4708         } break;
4709         case 6:
4710           ct = DM_POLYTOPE_HEXAHEDRON;
4711           break;
4712         default:
4713           break;
4714         }
4715         break;
4716       default:
4717         break;
4718       }
4719     } else if (pheight > 0) {
4720       switch (coneSize) {
4721       case 2:
4722         ct = DM_POLYTOPE_SEGMENT;
4723         break;
4724       case 3:
4725         ct = DM_POLYTOPE_TRIANGLE;
4726         break;
4727       case 4:
4728         ct = DM_POLYTOPE_QUADRILATERAL;
4729         break;
4730       default:
4731         break;
4732       }
4733     }
4734   }
4735   *pt = ct;
4736   PetscFunctionReturn(PETSC_SUCCESS);
4737 }
4738 
4739 /*@
4740   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4741 
4742   Collective
4743 
4744   Input Parameter:
4745 . dm - The `DMPLEX`
4746 
4747   Level: developer
4748 
4749   Note:
4750   This function is normally called automatically when a cell type is requested. It creates an
4751   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4752   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4753 
4754   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4755 
4756 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4757 @*/
4758 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4759 {
4760   DM_Plex *mesh;
4761   DMLabel  ctLabel;
4762   PetscInt pStart, pEnd, p;
4763 
4764   PetscFunctionBegin;
4765   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4766   mesh = (DM_Plex *)dm->data;
4767   PetscCall(DMCreateLabel(dm, "celltype"));
4768   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4769   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4770   PetscCall(PetscFree(mesh->cellTypes));
4771   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4772   for (p = pStart; p < pEnd; ++p) {
4773     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4774     PetscInt       pdepth;
4775 
4776     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4777     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4778     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]);
4779     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4780     mesh->cellTypes[p - pStart].value_as_uint8 = (uint8_t)ct;
4781   }
4782   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4783   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4784   PetscFunctionReturn(PETSC_SUCCESS);
4785 }
4786 
4787 /*@C
4788   DMPlexGetJoin - Get an array for the join of the set of points
4789 
4790   Not Collective
4791 
4792   Input Parameters:
4793 + dm        - The `DMPLEX` object
4794 . numPoints - The number of input points for the join
4795 - points    - The input points
4796 
4797   Output Parameters:
4798 + numCoveredPoints - The number of points in the join
4799 - coveredPoints    - The points in the join
4800 
4801   Level: intermediate
4802 
4803   Note:
4804   Currently, this is restricted to a single level join
4805 
4806   Fortran Notes:
4807   `converedPoints` must be declared with
4808 .vb
4809   PetscInt, pointer :: coveredPints(:)
4810 .ve
4811 
4812   The `numCoveredPoints` argument is not present in the Fortran binding.
4813 
4814 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4815 @*/
4816 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4817 {
4818   DM_Plex  *mesh = (DM_Plex *)dm->data;
4819   PetscInt *join[2];
4820   PetscInt  joinSize, i = 0;
4821   PetscInt  dof, off, p, c, m;
4822   PetscInt  maxSupportSize;
4823 
4824   PetscFunctionBegin;
4825   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4826   PetscAssertPointer(points, 3);
4827   PetscAssertPointer(numCoveredPoints, 4);
4828   PetscAssertPointer(coveredPoints, 5);
4829   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4830   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4831   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4832   /* Copy in support of first point */
4833   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4834   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4835   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4836   /* Check each successive support */
4837   for (p = 1; p < numPoints; ++p) {
4838     PetscInt newJoinSize = 0;
4839 
4840     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4841     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4842     for (c = 0; c < dof; ++c) {
4843       const PetscInt point = mesh->supports[off + c];
4844 
4845       for (m = 0; m < joinSize; ++m) {
4846         if (point == join[i][m]) {
4847           join[1 - i][newJoinSize++] = point;
4848           break;
4849         }
4850       }
4851     }
4852     joinSize = newJoinSize;
4853     i        = 1 - i;
4854   }
4855   *numCoveredPoints = joinSize;
4856   *coveredPoints    = join[i];
4857   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4858   PetscFunctionReturn(PETSC_SUCCESS);
4859 }
4860 
4861 /*@C
4862   DMPlexRestoreJoin - Restore an array for the join of the set of points obtained with `DMPlexGetJoin()`
4863 
4864   Not Collective
4865 
4866   Input Parameters:
4867 + dm        - The `DMPLEX` object
4868 . numPoints - The number of input points for the join
4869 - points    - The input points
4870 
4871   Output Parameters:
4872 + numCoveredPoints - The number of points in the join
4873 - coveredPoints    - The points in the join
4874 
4875   Level: intermediate
4876 
4877   Fortran Notes:
4878   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4879 
4880 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4881 @*/
4882 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4883 {
4884   PetscFunctionBegin;
4885   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4886   if (points) PetscAssertPointer(points, 3);
4887   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4888   PetscAssertPointer(coveredPoints, 5);
4889   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4890   if (numCoveredPoints) *numCoveredPoints = 0;
4891   PetscFunctionReturn(PETSC_SUCCESS);
4892 }
4893 
4894 /*@C
4895   DMPlexGetFullJoin - Get an array for the join of the set of points
4896 
4897   Not Collective
4898 
4899   Input Parameters:
4900 + dm        - The `DMPLEX` object
4901 . numPoints - The number of input points for the join
4902 - points    - The input points, its length is `numPoints`
4903 
4904   Output Parameters:
4905 + numCoveredPoints - The number of points in the join
4906 - coveredPoints    - The points in the join, its length is `numCoveredPoints`
4907 
4908   Level: intermediate
4909 
4910   Fortran Notes:
4911   `points` and `converedPoints` must be declared with
4912 .vb
4913   PetscInt, pointer :: points(:)
4914   PetscInt, pointer :: coveredPints(:)
4915 .ve
4916 
4917   The `numCoveredPoints` argument is not present in the Fortran binding.
4918 
4919 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4920 @*/
4921 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4922 {
4923   PetscInt *offsets, **closures;
4924   PetscInt *join[2];
4925   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4926   PetscInt  p, d, c, m, ms;
4927 
4928   PetscFunctionBegin;
4929   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4930   PetscAssertPointer(points, 3);
4931   PetscAssertPointer(numCoveredPoints, 4);
4932   PetscAssertPointer(coveredPoints, 5);
4933 
4934   PetscCall(DMPlexGetDepth(dm, &depth));
4935   PetscCall(PetscCalloc1(numPoints, &closures));
4936   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4937   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4938   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4939   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4940   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4941 
4942   for (p = 0; p < numPoints; ++p) {
4943     PetscInt closureSize;
4944 
4945     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4946 
4947     offsets[p * (depth + 2) + 0] = 0;
4948     for (d = 0; d < depth + 1; ++d) {
4949       PetscInt pStart, pEnd, i;
4950 
4951       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4952       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4953         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4954           offsets[p * (depth + 2) + d + 1] = i;
4955           break;
4956         }
4957       }
4958       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4959     }
4960     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);
4961   }
4962   for (d = 0; d < depth + 1; ++d) {
4963     PetscInt dof;
4964 
4965     /* Copy in support of first point */
4966     dof = offsets[d + 1] - offsets[d];
4967     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4968     /* Check each successive cone */
4969     for (p = 1; p < numPoints && joinSize; ++p) {
4970       PetscInt newJoinSize = 0;
4971 
4972       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4973       for (c = 0; c < dof; ++c) {
4974         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4975 
4976         for (m = 0; m < joinSize; ++m) {
4977           if (point == join[i][m]) {
4978             join[1 - i][newJoinSize++] = point;
4979             break;
4980           }
4981         }
4982       }
4983       joinSize = newJoinSize;
4984       i        = 1 - i;
4985     }
4986     if (joinSize) break;
4987   }
4988   *numCoveredPoints = joinSize;
4989   *coveredPoints    = join[i];
4990   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4991   PetscCall(PetscFree(closures));
4992   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4993   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4994   PetscFunctionReturn(PETSC_SUCCESS);
4995 }
4996 
4997 /*@C
4998   DMPlexGetMeet - Get an array for the meet of the set of points
4999 
5000   Not Collective
5001 
5002   Input Parameters:
5003 + dm        - The `DMPLEX` object
5004 . numPoints - The number of input points for the meet
5005 - points    - The input points, of length `numPoints`
5006 
5007   Output Parameters:
5008 + numCoveringPoints - The number of points in the meet
5009 - coveringPoints    - The points in the meet, of length `numCoveringPoints`
5010 
5011   Level: intermediate
5012 
5013   Note:
5014   Currently, this is restricted to a single level meet
5015 
5016   Fortran Notes:
5017   `coveringPoints` must be declared with
5018 .vb
5019   PetscInt, pointer :: coveringPoints(:)
5020 .ve
5021 
5022   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
5023 
5024 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
5025 @*/
5026 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt *coveringPoints[])
5027 {
5028   DM_Plex  *mesh = (DM_Plex *)dm->data;
5029   PetscInt *meet[2];
5030   PetscInt  meetSize, i = 0;
5031   PetscInt  dof, off, p, c, m;
5032   PetscInt  maxConeSize;
5033 
5034   PetscFunctionBegin;
5035   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5036   PetscAssertPointer(points, 3);
5037   PetscAssertPointer(numCoveringPoints, 4);
5038   PetscAssertPointer(coveringPoints, 5);
5039   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
5040   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
5041   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
5042   /* Copy in cone of first point */
5043   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
5044   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
5045   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
5046   /* Check each successive cone */
5047   for (p = 1; p < numPoints; ++p) {
5048     PetscInt newMeetSize = 0;
5049 
5050     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
5051     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
5052     for (c = 0; c < dof; ++c) {
5053       const PetscInt point = mesh->cones[off + c];
5054 
5055       for (m = 0; m < meetSize; ++m) {
5056         if (point == meet[i][m]) {
5057           meet[1 - i][newMeetSize++] = point;
5058           break;
5059         }
5060       }
5061     }
5062     meetSize = newMeetSize;
5063     i        = 1 - i;
5064   }
5065   *numCoveringPoints = meetSize;
5066   *coveringPoints    = meet[i];
5067   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
5068   PetscFunctionReturn(PETSC_SUCCESS);
5069 }
5070 
5071 /*@C
5072   DMPlexRestoreMeet - Restore an array for the meet of the set of points obtained with `DMPlexGetMeet()`
5073 
5074   Not Collective
5075 
5076   Input Parameters:
5077 + dm        - The `DMPLEX` object
5078 . numPoints - The number of input points for the meet
5079 - points    - The input points
5080 
5081   Output Parameters:
5082 + numCoveredPoints - The number of points in the meet
5083 - coveredPoints    - The points in the meet
5084 
5085   Level: intermediate
5086 
5087   Fortran Notes:
5088   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
5089 
5090 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
5091 @*/
5092 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
5093 {
5094   PetscFunctionBegin;
5095   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5096   if (points) PetscAssertPointer(points, 3);
5097   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
5098   PetscAssertPointer(coveredPoints, 5);
5099   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
5100   if (numCoveredPoints) *numCoveredPoints = 0;
5101   PetscFunctionReturn(PETSC_SUCCESS);
5102 }
5103 
5104 /*@C
5105   DMPlexGetFullMeet - Get an array for the meet of the set of points
5106 
5107   Not Collective
5108 
5109   Input Parameters:
5110 + dm        - The `DMPLEX` object
5111 . numPoints - The number of input points for the meet
5112 - points    - The input points, of length  `numPoints`
5113 
5114   Output Parameters:
5115 + numCoveredPoints - The number of points in the meet
5116 - coveredPoints    - The points in the meet, of length  `numCoveredPoints`
5117 
5118   Level: intermediate
5119 
5120   Fortran Notes:
5121   `points` and `coveredPoints` must be declared with
5122 .vb
5123   PetscInt, pointer :: points(:)
5124   PetscInt, pointer :: coveredPoints(:)
5125 .ve
5126 
5127   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
5128 
5129 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
5130 @*/
5131 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
5132 {
5133   PetscInt *offsets, **closures;
5134   PetscInt *meet[2];
5135   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
5136   PetscInt  p, h, c, m, mc;
5137 
5138   PetscFunctionBegin;
5139   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5140   PetscAssertPointer(points, 3);
5141   PetscAssertPointer(numCoveredPoints, 4);
5142   PetscAssertPointer(coveredPoints, 5);
5143 
5144   PetscCall(DMPlexGetDepth(dm, &height));
5145   PetscCall(PetscMalloc1(numPoints, &closures));
5146   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5147   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
5148   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
5149   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
5150   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
5151 
5152   for (p = 0; p < numPoints; ++p) {
5153     PetscInt closureSize;
5154 
5155     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
5156 
5157     offsets[p * (height + 2) + 0] = 0;
5158     for (h = 0; h < height + 1; ++h) {
5159       PetscInt pStart, pEnd, i;
5160 
5161       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
5162       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
5163         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
5164           offsets[p * (height + 2) + h + 1] = i;
5165           break;
5166         }
5167       }
5168       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
5169     }
5170     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);
5171   }
5172   for (h = 0; h < height + 1; ++h) {
5173     PetscInt dof;
5174 
5175     /* Copy in cone of first point */
5176     dof = offsets[h + 1] - offsets[h];
5177     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
5178     /* Check each successive cone */
5179     for (p = 1; p < numPoints && meetSize; ++p) {
5180       PetscInt newMeetSize = 0;
5181 
5182       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
5183       for (c = 0; c < dof; ++c) {
5184         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
5185 
5186         for (m = 0; m < meetSize; ++m) {
5187           if (point == meet[i][m]) {
5188             meet[1 - i][newMeetSize++] = point;
5189             break;
5190           }
5191         }
5192       }
5193       meetSize = newMeetSize;
5194       i        = 1 - i;
5195     }
5196     if (meetSize) break;
5197   }
5198   *numCoveredPoints = meetSize;
5199   *coveredPoints    = meet[i];
5200   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
5201   PetscCall(PetscFree(closures));
5202   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5203   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
5204   PetscFunctionReturn(PETSC_SUCCESS);
5205 }
5206 
5207 /*@
5208   DMPlexEqual - Determine if two `DM` have the same topology
5209 
5210   Not Collective
5211 
5212   Input Parameters:
5213 + dmA - A `DMPLEX` object
5214 - dmB - A `DMPLEX` object
5215 
5216   Output Parameter:
5217 . equal - `PETSC_TRUE` if the topologies are identical
5218 
5219   Level: intermediate
5220 
5221   Note:
5222   We are not solving graph isomorphism, so we do not permute.
5223 
5224 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5225 @*/
5226 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
5227 {
5228   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
5229 
5230   PetscFunctionBegin;
5231   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
5232   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
5233   PetscAssertPointer(equal, 3);
5234 
5235   *equal = PETSC_FALSE;
5236   PetscCall(DMPlexGetDepth(dmA, &depth));
5237   PetscCall(DMPlexGetDepth(dmB, &depthB));
5238   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
5239   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
5240   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
5241   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
5242   for (p = pStart; p < pEnd; ++p) {
5243     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
5244     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
5245 
5246     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
5247     PetscCall(DMPlexGetCone(dmA, p, &cone));
5248     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
5249     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
5250     PetscCall(DMPlexGetCone(dmB, p, &coneB));
5251     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
5252     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5253     for (c = 0; c < coneSize; ++c) {
5254       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5255       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5256     }
5257     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
5258     PetscCall(DMPlexGetSupport(dmA, p, &support));
5259     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
5260     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
5261     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5262     for (s = 0; s < supportSize; ++s) {
5263       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
5264     }
5265   }
5266   *equal = PETSC_TRUE;
5267   PetscFunctionReturn(PETSC_SUCCESS);
5268 }
5269 
5270 /*@
5271   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
5272 
5273   Not Collective
5274 
5275   Input Parameters:
5276 + dm         - The `DMPLEX`
5277 . cellDim    - The cell dimension
5278 - numCorners - The number of vertices on a cell
5279 
5280   Output Parameter:
5281 . numFaceVertices - The number of vertices on a face
5282 
5283   Level: developer
5284 
5285   Note:
5286   Of course this can only work for a restricted set of symmetric shapes
5287 
5288 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5289 @*/
5290 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
5291 {
5292   MPI_Comm comm;
5293 
5294   PetscFunctionBegin;
5295   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
5296   PetscAssertPointer(numFaceVertices, 4);
5297   switch (cellDim) {
5298   case 0:
5299     *numFaceVertices = 0;
5300     break;
5301   case 1:
5302     *numFaceVertices = 1;
5303     break;
5304   case 2:
5305     switch (numCorners) {
5306     case 3:                 /* triangle */
5307       *numFaceVertices = 2; /* Edge has 2 vertices */
5308       break;
5309     case 4:                 /* quadrilateral */
5310       *numFaceVertices = 2; /* Edge has 2 vertices */
5311       break;
5312     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
5313       *numFaceVertices = 3; /* Edge has 3 vertices */
5314       break;
5315     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
5316       *numFaceVertices = 3; /* Edge has 3 vertices */
5317       break;
5318     default:
5319       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5320     }
5321     break;
5322   case 3:
5323     switch (numCorners) {
5324     case 4:                 /* tetradehdron */
5325       *numFaceVertices = 3; /* Face has 3 vertices */
5326       break;
5327     case 6:                 /* tet cohesive cells */
5328       *numFaceVertices = 4; /* Face has 4 vertices */
5329       break;
5330     case 8:                 /* hexahedron */
5331       *numFaceVertices = 4; /* Face has 4 vertices */
5332       break;
5333     case 9:                 /* tet cohesive Lagrange cells */
5334       *numFaceVertices = 6; /* Face has 6 vertices */
5335       break;
5336     case 10:                /* quadratic tetrahedron */
5337       *numFaceVertices = 6; /* Face has 6 vertices */
5338       break;
5339     case 12:                /* hex cohesive Lagrange cells */
5340       *numFaceVertices = 6; /* Face has 6 vertices */
5341       break;
5342     case 18:                /* quadratic tet cohesive Lagrange cells */
5343       *numFaceVertices = 6; /* Face has 6 vertices */
5344       break;
5345     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
5346       *numFaceVertices = 9; /* Face has 9 vertices */
5347       break;
5348     default:
5349       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5350     }
5351     break;
5352   default:
5353     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
5354   }
5355   PetscFunctionReturn(PETSC_SUCCESS);
5356 }
5357 
5358 /*@
5359   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
5360 
5361   Not Collective
5362 
5363   Input Parameter:
5364 . dm - The `DMPLEX` object
5365 
5366   Output Parameter:
5367 . depthLabel - The `DMLabel` recording point depth
5368 
5369   Level: developer
5370 
5371 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
5372 @*/
5373 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
5374 {
5375   PetscFunctionBegin;
5376   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5377   PetscAssertPointer(depthLabel, 2);
5378   *depthLabel = dm->depthLabel;
5379   PetscFunctionReturn(PETSC_SUCCESS);
5380 }
5381 
5382 /*@
5383   DMPlexGetDepth - Get the depth of the DAG representing this mesh
5384 
5385   Not Collective
5386 
5387   Input Parameter:
5388 . dm - The `DMPLEX` object
5389 
5390   Output Parameter:
5391 . depth - The number of strata (breadth first levels) in the DAG
5392 
5393   Level: developer
5394 
5395   Notes:
5396   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
5397 
5398   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
5399 
5400   An empty mesh gives -1.
5401 
5402 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
5403 @*/
5404 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
5405 {
5406   DM_Plex *mesh = (DM_Plex *)dm->data;
5407   DMLabel  label;
5408   PetscInt d = -1;
5409 
5410   PetscFunctionBegin;
5411   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5412   PetscAssertPointer(depth, 2);
5413   if (mesh->tr) {
5414     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
5415   } else {
5416     PetscCall(DMPlexGetDepthLabel(dm, &label));
5417     // Allow missing depths
5418     if (label) PetscCall(DMLabelGetValueBounds(label, NULL, &d));
5419     *depth = d;
5420   }
5421   PetscFunctionReturn(PETSC_SUCCESS);
5422 }
5423 
5424 /*@
5425   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
5426 
5427   Not Collective
5428 
5429   Input Parameters:
5430 + dm    - The `DMPLEX` object
5431 - depth - The requested depth
5432 
5433   Output Parameters:
5434 + start - The first point at this `depth`
5435 - end   - One beyond the last point at this `depth`
5436 
5437   Level: developer
5438 
5439   Notes:
5440   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5441   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5442   higher dimension, e.g., "edges".
5443 
5444 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5445 @*/
5446 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
5447 {
5448   DM_Plex *mesh = (DM_Plex *)dm->data;
5449   DMLabel  label;
5450   PetscInt pStart, pEnd;
5451 
5452   PetscFunctionBegin;
5453   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5454   if (start) {
5455     PetscAssertPointer(start, 3);
5456     *start = 0;
5457   }
5458   if (end) {
5459     PetscAssertPointer(end, 4);
5460     *end = 0;
5461   }
5462   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5463   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5464   if (depth < 0) {
5465     if (start) *start = pStart;
5466     if (end) *end = pEnd;
5467     PetscFunctionReturn(PETSC_SUCCESS);
5468   }
5469   if (mesh->tr) {
5470     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5471   } else {
5472     PetscCall(DMPlexGetDepthLabel(dm, &label));
5473     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5474     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5475   }
5476   PetscFunctionReturn(PETSC_SUCCESS);
5477 }
5478 
5479 /*@
5480   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5481 
5482   Not Collective
5483 
5484   Input Parameters:
5485 + dm     - The `DMPLEX` object
5486 - height - The requested height
5487 
5488   Output Parameters:
5489 + start - The first point at this `height`
5490 - end   - One beyond the last point at this `height`
5491 
5492   Level: developer
5493 
5494   Notes:
5495   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5496   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5497   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5498 
5499 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5500 @*/
5501 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5502 {
5503   DMLabel  label;
5504   PetscInt depth, pStart, pEnd;
5505 
5506   PetscFunctionBegin;
5507   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5508   if (start) {
5509     PetscAssertPointer(start, 3);
5510     *start = 0;
5511   }
5512   if (end) {
5513     PetscAssertPointer(end, 4);
5514     *end = 0;
5515   }
5516   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5517   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5518   if (height < 0) {
5519     if (start) *start = pStart;
5520     if (end) *end = pEnd;
5521     PetscFunctionReturn(PETSC_SUCCESS);
5522   }
5523   PetscCall(DMPlexGetDepthLabel(dm, &label));
5524   if (label) PetscCall(DMLabelGetNumValues(label, &depth));
5525   else PetscCall(DMGetDimension(dm, &depth));
5526   PetscCheck(depth >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Depth not yet computed");
5527   PetscCall(DMPlexGetDepthStratum(dm, depth - 1 - height, start, end));
5528   PetscFunctionReturn(PETSC_SUCCESS);
5529 }
5530 
5531 /*@
5532   DMPlexGetPointDepth - Get the `depth` of a given point
5533 
5534   Not Collective
5535 
5536   Input Parameters:
5537 + dm    - The `DMPLEX` object
5538 - point - The point
5539 
5540   Output Parameter:
5541 . depth - The depth of the `point`
5542 
5543   Level: intermediate
5544 
5545 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5546 @*/
5547 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5548 {
5549   PetscFunctionBegin;
5550   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5551   PetscAssertPointer(depth, 3);
5552   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5553   PetscFunctionReturn(PETSC_SUCCESS);
5554 }
5555 
5556 /*@
5557   DMPlexGetPointHeight - Get the `height` of a given point
5558 
5559   Not Collective
5560 
5561   Input Parameters:
5562 + dm    - The `DMPLEX` object
5563 - point - The point
5564 
5565   Output Parameter:
5566 . height - The height of the `point`
5567 
5568   Level: intermediate
5569 
5570 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5571 @*/
5572 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5573 {
5574   PetscInt n, pDepth;
5575 
5576   PetscFunctionBegin;
5577   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5578   PetscAssertPointer(height, 3);
5579   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5580   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5581   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5582   PetscFunctionReturn(PETSC_SUCCESS);
5583 }
5584 
5585 /*@
5586   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5587 
5588   Not Collective
5589 
5590   Input Parameter:
5591 . dm - The `DMPLEX` object
5592 
5593   Output Parameter:
5594 . celltypeLabel - The `DMLabel` recording cell polytope type
5595 
5596   Level: developer
5597 
5598   Note:
5599   This function will trigger automatica computation of cell types. This can be disabled by calling
5600   `DMCreateLabel`(dm, "celltype") beforehand.
5601 
5602 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5603 @*/
5604 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5605 {
5606   PetscFunctionBegin;
5607   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5608   PetscAssertPointer(celltypeLabel, 2);
5609   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5610   *celltypeLabel = dm->celltypeLabel;
5611   PetscFunctionReturn(PETSC_SUCCESS);
5612 }
5613 
5614 /*@
5615   DMPlexGetCellType - Get the polytope type of a given cell
5616 
5617   Not Collective
5618 
5619   Input Parameters:
5620 + dm   - The `DMPLEX` object
5621 - cell - The cell
5622 
5623   Output Parameter:
5624 . celltype - The polytope type of the cell
5625 
5626   Level: intermediate
5627 
5628 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5629 @*/
5630 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5631 {
5632   DM_Plex *mesh = (DM_Plex *)dm->data;
5633   DMLabel  label;
5634   PetscInt ct;
5635 
5636   PetscFunctionBegin;
5637   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5638   PetscAssertPointer(celltype, 3);
5639   if (mesh->tr) {
5640     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5641   } else {
5642     PetscInt pStart, pEnd;
5643 
5644     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5645     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5646       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5647       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5648       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5649       for (PetscInt p = pStart; p < pEnd; p++) {
5650         PetscCall(DMLabelGetValue(label, p, &ct));
5651         mesh->cellTypes[p - pStart].value_as_uint8 = (uint8_t)ct;
5652       }
5653     }
5654     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5655     if (PetscDefined(USE_DEBUG)) {
5656       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5657       PetscCall(DMLabelGetValue(label, cell, &ct));
5658       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5659       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5660     }
5661   }
5662   PetscFunctionReturn(PETSC_SUCCESS);
5663 }
5664 
5665 /*@
5666   DMPlexSetCellType - Set the polytope type of a given cell
5667 
5668   Not Collective
5669 
5670   Input Parameters:
5671 + dm       - The `DMPLEX` object
5672 . cell     - The cell
5673 - celltype - The polytope type of the cell
5674 
5675   Level: advanced
5676 
5677   Note:
5678   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5679   is executed. This function will override the computed type. However, if automatic classification will not succeed
5680   and a user wants to manually specify all types, the classification must be disabled by calling
5681   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5682 
5683 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5684 @*/
5685 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5686 {
5687   DM_Plex *mesh = (DM_Plex *)dm->data;
5688   DMLabel  label;
5689   PetscInt pStart, pEnd;
5690 
5691   PetscFunctionBegin;
5692   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5693   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5694   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5695   PetscCall(DMLabelSetValue(label, cell, celltype));
5696   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5697   mesh->cellTypes[cell - pStart].value_as_uint8 = (uint8_t)celltype;
5698   PetscFunctionReturn(PETSC_SUCCESS);
5699 }
5700 
5701 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5702 {
5703   PetscSection section;
5704   PetscInt     maxHeight;
5705   const char  *prefix;
5706 
5707   PetscFunctionBegin;
5708   PetscCall(DMClone(dm, cdm));
5709   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5710   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5711   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5712   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5713   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5714   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5715   PetscCall(DMSetLocalSection(*cdm, section));
5716   PetscCall(PetscSectionDestroy(&section));
5717 
5718   PetscCall(DMSetNumFields(*cdm, 1));
5719   PetscCall(DMCreateDS(*cdm));
5720   (*cdm)->cloneOpts = PETSC_TRUE;
5721   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5722   PetscFunctionReturn(PETSC_SUCCESS);
5723 }
5724 
5725 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5726 {
5727   Vec coordsLocal, cellCoordsLocal;
5728   DM  coordsDM, cellCoordsDM;
5729 
5730   PetscFunctionBegin;
5731   *field = NULL;
5732   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5733   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5734   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5735   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5736   if (coordsLocal && coordsDM) {
5737     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5738     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5739   }
5740   PetscFunctionReturn(PETSC_SUCCESS);
5741 }
5742 
5743 /*@
5744   DMPlexGetConeSection - Return a section which describes the layout of cone data
5745 
5746   Not Collective
5747 
5748   Input Parameter:
5749 . dm - The `DMPLEX` object
5750 
5751   Output Parameter:
5752 . section - The `PetscSection` object
5753 
5754   Level: developer
5755 
5756 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5757 @*/
5758 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5759 {
5760   DM_Plex *mesh = (DM_Plex *)dm->data;
5761 
5762   PetscFunctionBegin;
5763   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5764   if (section) *section = mesh->coneSection;
5765   PetscFunctionReturn(PETSC_SUCCESS);
5766 }
5767 
5768 /*@
5769   DMPlexGetSupportSection - Return a section which describes the layout of support data
5770 
5771   Not Collective
5772 
5773   Input Parameter:
5774 . dm - The `DMPLEX` object
5775 
5776   Output Parameter:
5777 . section - The `PetscSection` object
5778 
5779   Level: developer
5780 
5781 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5782 @*/
5783 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5784 {
5785   DM_Plex *mesh = (DM_Plex *)dm->data;
5786 
5787   PetscFunctionBegin;
5788   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5789   if (section) *section = mesh->supportSection;
5790   PetscFunctionReturn(PETSC_SUCCESS);
5791 }
5792 
5793 /*@C
5794   DMPlexGetCones - Return cone data
5795 
5796   Not Collective
5797 
5798   Input Parameter:
5799 . dm - The `DMPLEX` object
5800 
5801   Output Parameter:
5802 . cones - The cone for each point
5803 
5804   Level: developer
5805 
5806 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5807 @*/
5808 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5809 {
5810   DM_Plex *mesh = (DM_Plex *)dm->data;
5811 
5812   PetscFunctionBegin;
5813   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5814   if (cones) *cones = mesh->cones;
5815   PetscFunctionReturn(PETSC_SUCCESS);
5816 }
5817 
5818 /*@C
5819   DMPlexGetConeOrientations - Return cone orientation data
5820 
5821   Not Collective
5822 
5823   Input Parameter:
5824 . dm - The `DMPLEX` object
5825 
5826   Output Parameter:
5827 . coneOrientations - The array of cone orientations for all points
5828 
5829   Level: developer
5830 
5831   Notes:
5832   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points
5833   as returned by `DMPlexGetConeOrientation()`.
5834 
5835   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5836 
5837 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5838 @*/
5839 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5840 {
5841   DM_Plex *mesh = (DM_Plex *)dm->data;
5842 
5843   PetscFunctionBegin;
5844   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5845   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5846   PetscFunctionReturn(PETSC_SUCCESS);
5847 }
5848 
5849 /* FEM Support */
5850 
5851 PetscErrorCode DMPlexGetAllCells_Internal(DM plex, IS *cellIS)
5852 {
5853   PetscInt depth;
5854 
5855   PetscFunctionBegin;
5856   PetscCall(DMPlexGetDepth(plex, &depth));
5857   PetscCall(DMGetStratumIS(plex, "dim", depth, cellIS));
5858   if (!*cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, cellIS));
5859   PetscFunctionReturn(PETSC_SUCCESS);
5860 }
5861 
5862 PetscErrorCode DMPlexGetAllFaces_Internal(DM plex, IS *faceIS)
5863 {
5864   PetscInt depth;
5865 
5866   PetscFunctionBegin;
5867   PetscCall(DMPlexGetDepth(plex, &depth));
5868   PetscCall(DMGetStratumIS(plex, "dim", depth - 1, faceIS));
5869   if (!*faceIS) PetscCall(DMGetStratumIS(plex, "depth", depth - 1, faceIS));
5870   PetscFunctionReturn(PETSC_SUCCESS);
5871 }
5872 
5873 /*
5874  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5875  representing a line in the section.
5876 */
5877 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(DM dm, PetscSection section, PetscInt field, PetscInt line, PetscInt *Nc, PetscInt *k, PetscBool *continuous, PetscBool *tensor)
5878 {
5879   PetscObject  obj;
5880   PetscClassId id;
5881   PetscFE      fe = NULL;
5882 
5883   PetscFunctionBeginHot;
5884   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5885   PetscCall(DMGetField(dm, field, NULL, &obj));
5886   PetscCall(PetscObjectGetClassId(obj, &id));
5887   if (id == PETSCFE_CLASSID) fe = (PetscFE)obj;
5888 
5889   if (!fe) {
5890     /* Assume the full interpolated mesh is in the chart; lines in particular */
5891     /* An order k SEM disc has k-1 dofs on an edge */
5892     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5893     *k = *k / *Nc + 1;
5894   } else {
5895     PetscInt       dual_space_size, dim;
5896     PetscDualSpace dsp;
5897 
5898     PetscCall(DMGetDimension(dm, &dim));
5899     PetscCall(PetscFEGetDualSpace(fe, &dsp));
5900     PetscCall(PetscDualSpaceGetDimension(dsp, &dual_space_size));
5901     *k = (PetscInt)PetscCeilReal(PetscPowReal(dual_space_size / *Nc, 1.0 / dim)) - 1;
5902     PetscCall(PetscDualSpaceLagrangeGetContinuity(dsp, continuous));
5903     PetscCall(PetscDualSpaceLagrangeGetTensor(dsp, tensor));
5904   }
5905   PetscFunctionReturn(PETSC_SUCCESS);
5906 }
5907 
5908 static PetscErrorCode GetFieldSize_Private(PetscInt dim, PetscInt k, PetscBool tensor, PetscInt *dof)
5909 {
5910   PetscFunctionBeginHot;
5911   if (tensor) {
5912     *dof = PetscPowInt(k + 1, dim);
5913   } else {
5914     switch (dim) {
5915     case 1:
5916       *dof = k + 1;
5917       break;
5918     case 2:
5919       *dof = ((k + 1) * (k + 2)) / 2;
5920       break;
5921     case 3:
5922       *dof = ((k + 1) * (k + 2) * (k + 3)) / 6;
5923       break;
5924     default:
5925       *dof = 0;
5926     }
5927   }
5928   PetscFunctionReturn(PETSC_SUCCESS);
5929 }
5930 
5931 /*@
5932   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5933   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5934   section provided (or the section of the `DM`).
5935 
5936   Input Parameters:
5937 + dm      - The `DM`
5938 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5939 - section - The `PetscSection` to reorder, or `NULL` for the default section
5940 
5941   Example:
5942   A typical interpolated single-quad mesh might order points as
5943 .vb
5944   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5945 
5946   v4 -- e6 -- v3
5947   |           |
5948   e7    c0    e8
5949   |           |
5950   v1 -- e5 -- v2
5951 .ve
5952 
5953   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5954   dofs in the order of points, e.g.,
5955 .vb
5956     c0 -> [0,1,2,3]
5957     v1 -> [4]
5958     ...
5959     e5 -> [8, 9]
5960 .ve
5961 
5962   which corresponds to the dofs
5963 .vb
5964     6   10  11  7
5965     13  2   3   15
5966     12  0   1   14
5967     4   8   9   5
5968 .ve
5969 
5970   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5971 .vb
5972   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5973 .ve
5974 
5975   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5976 .vb
5977    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5978 .ve
5979 
5980   Level: developer
5981 
5982   Notes:
5983   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5984   degree of the basis.
5985 
5986   This is required to run with libCEED.
5987 
5988 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5989 @*/
5990 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5991 {
5992   DMLabel   label;
5993   PetscInt  dim, depth = -1, eStart = -1, Nf;
5994   PetscBool continuous = PETSC_TRUE, tensor = PETSC_TRUE;
5995 
5996   PetscFunctionBegin;
5997   PetscCall(DMGetDimension(dm, &dim));
5998   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5999   if (point < 0) {
6000     PetscInt sStart, sEnd;
6001 
6002     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
6003     point = sEnd - sStart ? sStart : point;
6004   }
6005   PetscCall(DMPlexGetDepthLabel(dm, &label));
6006   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
6007   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6008   if (depth == 1) {
6009     eStart = point;
6010   } else if (depth == dim) {
6011     const PetscInt *cone;
6012 
6013     PetscCall(DMPlexGetCone(dm, point, &cone));
6014     if (dim == 2) eStart = cone[0];
6015     else if (dim == 3) {
6016       const PetscInt *cone2;
6017       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
6018       eStart = cone2[0];
6019     } 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);
6020   } 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);
6021 
6022   PetscCall(PetscSectionGetNumFields(section, &Nf));
6023   for (PetscInt d = 1; d <= dim; d++) {
6024     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
6025     PetscInt *perm;
6026 
6027     for (f = 0; f < Nf; ++f) {
6028       PetscInt dof;
6029 
6030       PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6031       PetscCheck(dim == 1 || tensor || !continuous, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Continuous field %" PetscInt_FMT " must have a tensor product discretization", f);
6032       if (!continuous && d < dim) continue;
6033       PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6034       size += dof * Nc;
6035     }
6036     PetscCall(PetscMalloc1(size, &perm));
6037     for (f = 0; f < Nf; ++f) {
6038       switch (d) {
6039       case 1:
6040         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6041         if (!continuous && d < dim) continue;
6042         /*
6043          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
6044          We want              [ vtx0; edge of length k-1; vtx1 ]
6045          */
6046         if (continuous) {
6047           for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
6048           for (i = 0; i < k - 1; i++)
6049             for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
6050           for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
6051           foffset = offset;
6052         } else {
6053           PetscInt dof;
6054 
6055           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6056           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6057           foffset = offset;
6058         }
6059         break;
6060       case 2:
6061         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
6062         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6063         if (!continuous && d < dim) continue;
6064         /* The SEM order is
6065 
6066          v_lb, {e_b}, v_rb,
6067          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
6068          v_lt, reverse {e_t}, v_rt
6069          */
6070         if (continuous) {
6071           const PetscInt of   = 0;
6072           const PetscInt oeb  = of + PetscSqr(k - 1);
6073           const PetscInt oer  = oeb + (k - 1);
6074           const PetscInt oet  = oer + (k - 1);
6075           const PetscInt oel  = oet + (k - 1);
6076           const PetscInt ovlb = oel + (k - 1);
6077           const PetscInt ovrb = ovlb + 1;
6078           const PetscInt ovrt = ovrb + 1;
6079           const PetscInt ovlt = ovrt + 1;
6080           PetscInt       o;
6081 
6082           /* bottom */
6083           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
6084           for (o = oeb; o < oer; ++o)
6085             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6086           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
6087           /* middle */
6088           for (i = 0; i < k - 1; ++i) {
6089             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
6090             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
6091               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6092             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
6093           }
6094           /* top */
6095           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
6096           for (o = oel - 1; o >= oet; --o)
6097             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6098           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
6099           foffset = offset;
6100         } else {
6101           PetscInt dof;
6102 
6103           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6104           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6105           foffset = offset;
6106         }
6107         break;
6108       case 3:
6109         /* The original hex closure is
6110 
6111          {c,
6112          f_b, f_t, f_f, f_b, f_r, f_l,
6113          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
6114          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
6115          */
6116         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6117         if (!continuous && d < dim) continue;
6118         /* The SEM order is
6119          Bottom Slice
6120          v_blf, {e^{(k-1)-n}_bf}, v_brf,
6121          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
6122          v_blb, {e_bb}, v_brb,
6123 
6124          Middle Slice (j)
6125          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
6126          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
6127          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
6128 
6129          Top Slice
6130          v_tlf, {e_tf}, v_trf,
6131          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
6132          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
6133          */
6134         if (continuous) {
6135           const PetscInt oc    = 0;
6136           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
6137           const PetscInt oft   = ofb + PetscSqr(k - 1);
6138           const PetscInt off   = oft + PetscSqr(k - 1);
6139           const PetscInt ofk   = off + PetscSqr(k - 1);
6140           const PetscInt ofr   = ofk + PetscSqr(k - 1);
6141           const PetscInt ofl   = ofr + PetscSqr(k - 1);
6142           const PetscInt oebl  = ofl + PetscSqr(k - 1);
6143           const PetscInt oebb  = oebl + (k - 1);
6144           const PetscInt oebr  = oebb + (k - 1);
6145           const PetscInt oebf  = oebr + (k - 1);
6146           const PetscInt oetf  = oebf + (k - 1);
6147           const PetscInt oetr  = oetf + (k - 1);
6148           const PetscInt oetb  = oetr + (k - 1);
6149           const PetscInt oetl  = oetb + (k - 1);
6150           const PetscInt oerf  = oetl + (k - 1);
6151           const PetscInt oelf  = oerf + (k - 1);
6152           const PetscInt oelb  = oelf + (k - 1);
6153           const PetscInt oerb  = oelb + (k - 1);
6154           const PetscInt ovblf = oerb + (k - 1);
6155           const PetscInt ovblb = ovblf + 1;
6156           const PetscInt ovbrb = ovblb + 1;
6157           const PetscInt ovbrf = ovbrb + 1;
6158           const PetscInt ovtlf = ovbrf + 1;
6159           const PetscInt ovtrf = ovtlf + 1;
6160           const PetscInt ovtrb = ovtrf + 1;
6161           const PetscInt ovtlb = ovtrb + 1;
6162           PetscInt       o, n;
6163 
6164           /* Bottom Slice */
6165           /*   bottom */
6166           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
6167           for (o = oetf - 1; o >= oebf; --o)
6168             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6169           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
6170           /*   middle */
6171           for (i = 0; i < k - 1; ++i) {
6172             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
6173             for (n = 0; n < k - 1; ++n) {
6174               o = ofb + n * (k - 1) + i;
6175               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6176             }
6177             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
6178           }
6179           /*   top */
6180           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
6181           for (o = oebb; o < oebr; ++o)
6182             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6183           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
6184 
6185           /* Middle Slice */
6186           for (j = 0; j < k - 1; ++j) {
6187             /*   bottom */
6188             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
6189             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
6190               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6191             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
6192             /*   middle */
6193             for (i = 0; i < k - 1; ++i) {
6194               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
6195               for (n = 0; n < k - 1; ++n)
6196                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
6197               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
6198             }
6199             /*   top */
6200             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
6201             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
6202               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6203             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
6204           }
6205 
6206           /* Top Slice */
6207           /*   bottom */
6208           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
6209           for (o = oetf; o < oetr; ++o)
6210             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6211           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
6212           /*   middle */
6213           for (i = 0; i < k - 1; ++i) {
6214             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
6215             for (n = 0; n < k - 1; ++n)
6216               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
6217             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
6218           }
6219           /*   top */
6220           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
6221           for (o = oetl - 1; o >= oetb; --o)
6222             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6223           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
6224 
6225           foffset = offset;
6226         } else {
6227           PetscInt dof;
6228 
6229           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6230           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6231           foffset = offset;
6232         }
6233         break;
6234       default:
6235         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
6236       }
6237     }
6238     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
6239     /* Check permutation */
6240     {
6241       PetscInt *check;
6242 
6243       PetscCall(PetscMalloc1(size, &check));
6244       for (i = 0; i < size; ++i) {
6245         check[i] = -1;
6246         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
6247       }
6248       for (i = 0; i < size; ++i) check[perm[i]] = i;
6249       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
6250       PetscCall(PetscFree(check));
6251     }
6252     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
6253     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
6254       PetscInt *loc_perm;
6255       PetscCall(PetscMalloc1(size * 2, &loc_perm));
6256       for (PetscInt i = 0; i < size; i++) {
6257         loc_perm[i]        = perm[i];
6258         loc_perm[size + i] = size + perm[i];
6259       }
6260       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
6261     }
6262   }
6263   PetscFunctionReturn(PETSC_SUCCESS);
6264 }
6265 
6266 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
6267 {
6268   PetscDS  prob;
6269   PetscInt depth, Nf, h;
6270   DMLabel  label;
6271 
6272   PetscFunctionBeginHot;
6273   PetscCall(DMGetDS(dm, &prob));
6274   Nf      = prob->Nf;
6275   label   = dm->depthLabel;
6276   *dspace = NULL;
6277   if (field < Nf) {
6278     PetscObject disc = prob->disc[field];
6279 
6280     if (disc->classid == PETSCFE_CLASSID) {
6281       PetscDualSpace dsp;
6282 
6283       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
6284       PetscCall(DMLabelGetNumValues(label, &depth));
6285       PetscCall(DMLabelGetValue(label, point, &h));
6286       h = depth - 1 - h;
6287       if (h) {
6288         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
6289       } else {
6290         *dspace = dsp;
6291       }
6292     }
6293   }
6294   PetscFunctionReturn(PETSC_SUCCESS);
6295 }
6296 
6297 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6298 {
6299   PetscScalar       *array;
6300   const PetscScalar *vArray;
6301   const PetscInt    *cone, *coneO;
6302   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
6303 
6304   PetscFunctionBeginHot;
6305   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6306   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6307   PetscCall(DMPlexGetCone(dm, point, &cone));
6308   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6309   if (!values || !*values) {
6310     if ((point >= pStart) && (point < pEnd)) {
6311       PetscInt dof;
6312 
6313       PetscCall(PetscSectionGetDof(section, point, &dof));
6314       size += dof;
6315     }
6316     for (p = 0; p < numPoints; ++p) {
6317       const PetscInt cp = cone[p];
6318       PetscInt       dof;
6319 
6320       if ((cp < pStart) || (cp >= pEnd)) continue;
6321       PetscCall(PetscSectionGetDof(section, cp, &dof));
6322       size += dof;
6323     }
6324     if (!values) {
6325       if (csize) *csize = size;
6326       PetscFunctionReturn(PETSC_SUCCESS);
6327     }
6328     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
6329   } else {
6330     array = *values;
6331   }
6332   size = 0;
6333   PetscCall(VecGetArrayRead(v, &vArray));
6334   if ((point >= pStart) && (point < pEnd)) {
6335     PetscInt           dof, off, d;
6336     const PetscScalar *varr;
6337 
6338     PetscCall(PetscSectionGetDof(section, point, &dof));
6339     PetscCall(PetscSectionGetOffset(section, point, &off));
6340     varr = PetscSafePointerPlusOffset(vArray, off);
6341     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6342     size += dof;
6343   }
6344   for (p = 0; p < numPoints; ++p) {
6345     const PetscInt     cp = cone[p];
6346     PetscInt           o  = coneO[p];
6347     PetscInt           dof, off, d;
6348     const PetscScalar *varr;
6349 
6350     if ((cp < pStart) || (cp >= pEnd)) continue;
6351     PetscCall(PetscSectionGetDof(section, cp, &dof));
6352     PetscCall(PetscSectionGetOffset(section, cp, &off));
6353     varr = PetscSafePointerPlusOffset(vArray, off);
6354     if (o >= 0) {
6355       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6356     } else {
6357       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
6358     }
6359     size += dof;
6360   }
6361   PetscCall(VecRestoreArrayRead(v, &vArray));
6362   if (!*values) {
6363     if (csize) *csize = size;
6364     *values = array;
6365   } else {
6366     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6367     *csize = size;
6368   }
6369   PetscFunctionReturn(PETSC_SUCCESS);
6370 }
6371 
6372 /* Compress out points not in the section */
6373 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
6374 {
6375   const PetscInt np = *numPoints;
6376   PetscInt       pStart, pEnd, p, q;
6377 
6378   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6379   for (p = 0, q = 0; p < np; ++p) {
6380     const PetscInt r = points[p * 2];
6381     if ((r >= pStart) && (r < pEnd)) {
6382       points[q * 2]     = r;
6383       points[q * 2 + 1] = points[p * 2 + 1];
6384       ++q;
6385     }
6386   }
6387   *numPoints = q;
6388   return PETSC_SUCCESS;
6389 }
6390 
6391 /* Compressed closure does not apply closure permutation */
6392 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6393 {
6394   const PetscInt *cla = NULL;
6395   PetscInt        np, *pts = NULL;
6396 
6397   PetscFunctionBeginHot;
6398   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
6399   if (!ornt && *clPoints) {
6400     PetscInt dof, off;
6401 
6402     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
6403     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
6404     PetscCall(ISGetIndices(*clPoints, &cla));
6405     np  = dof / 2;
6406     pts = PetscSafePointerPlusOffset((PetscInt *)cla, off);
6407   } else {
6408     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
6409     PetscCall(CompressPoints_Private(section, &np, pts));
6410   }
6411   *numPoints = np;
6412   *points    = pts;
6413   *clp       = cla;
6414   PetscFunctionReturn(PETSC_SUCCESS);
6415 }
6416 
6417 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6418 {
6419   PetscFunctionBeginHot;
6420   if (!*clPoints) {
6421     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
6422   } else {
6423     PetscCall(ISRestoreIndices(*clPoints, clp));
6424   }
6425   *numPoints = 0;
6426   *points    = NULL;
6427   *clSec     = NULL;
6428   *clPoints  = NULL;
6429   *clp       = NULL;
6430   PetscFunctionReturn(PETSC_SUCCESS);
6431 }
6432 
6433 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
6434 {
6435   PetscInt            offset = 0, p;
6436   const PetscInt    **perms  = NULL;
6437   const PetscScalar **flips  = NULL;
6438 
6439   PetscFunctionBeginHot;
6440   *size = 0;
6441   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6442   for (p = 0; p < numPoints; p++) {
6443     const PetscInt     point = points[2 * p];
6444     const PetscInt    *perm  = perms ? perms[p] : NULL;
6445     const PetscScalar *flip  = flips ? flips[p] : NULL;
6446     PetscInt           dof, off, d;
6447     const PetscScalar *varr;
6448 
6449     PetscCall(PetscSectionGetDof(section, point, &dof));
6450     PetscCall(PetscSectionGetOffset(section, point, &off));
6451     varr = PetscSafePointerPlusOffset(vArray, off);
6452     if (clperm) {
6453       if (perm) {
6454         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
6455       } else {
6456         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
6457       }
6458       if (flip) {
6459         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
6460       }
6461     } else {
6462       if (perm) {
6463         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
6464       } else {
6465         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
6466       }
6467       if (flip) {
6468         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
6469       }
6470     }
6471     offset += dof;
6472   }
6473   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6474   *size = offset;
6475   PetscFunctionReturn(PETSC_SUCCESS);
6476 }
6477 
6478 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[])
6479 {
6480   PetscInt offset = 0, f;
6481 
6482   PetscFunctionBeginHot;
6483   *size = 0;
6484   for (f = 0; f < numFields; ++f) {
6485     PetscInt            p;
6486     const PetscInt    **perms = NULL;
6487     const PetscScalar **flips = NULL;
6488 
6489     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6490     for (p = 0; p < numPoints; p++) {
6491       const PetscInt     point = points[2 * p];
6492       PetscInt           fdof, foff, b;
6493       const PetscScalar *varr;
6494       const PetscInt    *perm = perms ? perms[p] : NULL;
6495       const PetscScalar *flip = flips ? flips[p] : NULL;
6496 
6497       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6498       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6499       varr = &vArray[foff];
6500       if (clperm) {
6501         if (perm) {
6502           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6503         } else {
6504           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6505         }
6506         if (flip) {
6507           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6508         }
6509       } else {
6510         if (perm) {
6511           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6512         } else {
6513           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6514         }
6515         if (flip) {
6516           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6517         }
6518       }
6519       offset += fdof;
6520     }
6521     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6522   }
6523   *size = offset;
6524   PetscFunctionReturn(PETSC_SUCCESS);
6525 }
6526 
6527 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, PetscBool useClPerm, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6528 {
6529   PetscSection    clSection;
6530   IS              clPoints;
6531   PetscInt       *points = NULL;
6532   const PetscInt *clp, *perm = NULL;
6533   PetscInt        depth, numFields, numPoints, asize;
6534 
6535   PetscFunctionBeginHot;
6536   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6537   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6538   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6539   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
6540   PetscCall(DMPlexGetDepth(dm, &depth));
6541   PetscCall(PetscSectionGetNumFields(section, &numFields));
6542   if (depth == 1 && numFields < 2) {
6543     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6544     PetscFunctionReturn(PETSC_SUCCESS);
6545   }
6546   /* Get points */
6547   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6548   /* Get sizes */
6549   asize = 0;
6550   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6551     PetscInt dof;
6552     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6553     asize += dof;
6554   }
6555   if (values) {
6556     const PetscScalar *vArray;
6557     PetscInt           size;
6558 
6559     if (*values) {
6560       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);
6561     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6562     if (useClPerm) PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6563     PetscCall(VecGetArrayRead(v, &vArray));
6564     /* Get values */
6565     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6566     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6567     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6568     /* Cleanup array */
6569     PetscCall(VecRestoreArrayRead(v, &vArray));
6570   }
6571   if (csize) *csize = asize;
6572   /* Cleanup points */
6573   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6574   PetscFunctionReturn(PETSC_SUCCESS);
6575 }
6576 
6577 /*@C
6578   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6579 
6580   Not collective
6581 
6582   Input Parameters:
6583 + dm      - The `DM`
6584 . section - The section describing the layout in `v`, or `NULL` to use the default section
6585 . v       - The local vector
6586 - point   - The point in the `DM`
6587 
6588   Input/Output Parameters:
6589 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6590 - values - An array to use for the values, or *values = `NULL` to have it allocated automatically;
6591            if the user provided `NULL`, it is a borrowed array and should not be freed, use  `DMPlexVecRestoreClosure()` to return it
6592 
6593   Level: intermediate
6594 
6595   Notes:
6596   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6597   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6598   assembly function, and a user may already have allocated storage for this operation.
6599 
6600   A typical use could be
6601 .vb
6602    values = NULL;
6603    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6604    for (cl = 0; cl < clSize; ++cl) {
6605      <Compute on closure>
6606    }
6607    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6608 .ve
6609   or
6610 .vb
6611    PetscMalloc1(clMaxSize, &values);
6612    for (p = pStart; p < pEnd; ++p) {
6613      clSize = clMaxSize;
6614      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6615      for (cl = 0; cl < clSize; ++cl) {
6616        <Compute on closure>
6617      }
6618    }
6619    PetscFree(values);
6620 .ve
6621 
6622   Fortran Notes:
6623   The `csize` argument is not present in the Fortran binding.
6624 
6625   `values` must be declared with
6626 .vb
6627   PetscScalar,dimension(:),pointer   :: values
6628 .ve
6629   and it will be allocated internally by PETSc to hold the values returned
6630 
6631 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6632 @*/
6633 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6634 {
6635   PetscFunctionBeginHot;
6636   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6637   PetscFunctionReturn(PETSC_SUCCESS);
6638 }
6639 
6640 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6641 {
6642   DMLabel            depthLabel;
6643   PetscSection       clSection;
6644   IS                 clPoints;
6645   PetscScalar       *array;
6646   const PetscScalar *vArray;
6647   PetscInt          *points = NULL;
6648   const PetscInt    *clp, *perm = NULL;
6649   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6650 
6651   PetscFunctionBeginHot;
6652   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6653   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6654   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6655   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6656   PetscCall(DMPlexGetDepth(dm, &mdepth));
6657   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6658   PetscCall(PetscSectionGetNumFields(section, &numFields));
6659   if (mdepth == 1 && numFields < 2) {
6660     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6661     PetscFunctionReturn(PETSC_SUCCESS);
6662   }
6663   /* Get points */
6664   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6665   for (clsize = 0, p = 0; p < Np; p++) {
6666     PetscInt dof;
6667     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6668     clsize += dof;
6669   }
6670   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6671   /* Filter points */
6672   for (p = 0; p < numPoints * 2; p += 2) {
6673     PetscInt dep;
6674 
6675     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6676     if (dep != depth) continue;
6677     points[Np * 2 + 0] = points[p];
6678     points[Np * 2 + 1] = points[p + 1];
6679     ++Np;
6680   }
6681   /* Get array */
6682   if (!values || !*values) {
6683     PetscInt asize = 0, dof;
6684 
6685     for (p = 0; p < Np * 2; p += 2) {
6686       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6687       asize += dof;
6688     }
6689     if (!values) {
6690       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6691       if (csize) *csize = asize;
6692       PetscFunctionReturn(PETSC_SUCCESS);
6693     }
6694     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6695   } else {
6696     array = *values;
6697   }
6698   PetscCall(VecGetArrayRead(v, &vArray));
6699   /* Get values */
6700   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6701   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6702   /* Cleanup points */
6703   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6704   /* Cleanup array */
6705   PetscCall(VecRestoreArrayRead(v, &vArray));
6706   if (!*values) {
6707     if (csize) *csize = size;
6708     *values = array;
6709   } else {
6710     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6711     *csize = size;
6712   }
6713   PetscFunctionReturn(PETSC_SUCCESS);
6714 }
6715 
6716 /*@C
6717   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point' obtained with `DMPlexVecGetClosure()`
6718 
6719   Not collective
6720 
6721   Input Parameters:
6722 + dm      - The `DM`
6723 . section - The section describing the layout in `v`, or `NULL` to use the default section
6724 . v       - The local vector
6725 . point   - The point in the `DM`
6726 . csize   - The number of values in the closure, or `NULL`
6727 - values  - The array of values
6728 
6729   Level: intermediate
6730 
6731   Note:
6732   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6733 
6734   Fortran Note:
6735   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6736 
6737 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6738 @*/
6739 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6740 {
6741   PetscInt size = 0;
6742 
6743   PetscFunctionBegin;
6744   /* Should work without recalculating size */
6745   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6746   *values = NULL;
6747   PetscFunctionReturn(PETSC_SUCCESS);
6748 }
6749 
6750 static inline void add(PetscScalar *x, PetscScalar y)
6751 {
6752   *x += y;
6753 }
6754 static inline void insert(PetscScalar *x, PetscScalar y)
6755 {
6756   *x = y;
6757 }
6758 
6759 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[])
6760 {
6761   PetscInt        cdof;  /* The number of constraints on this point */
6762   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6763   PetscScalar    *a;
6764   PetscInt        off, cind = 0, k;
6765 
6766   PetscFunctionBegin;
6767   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6768   PetscCall(PetscSectionGetOffset(section, point, &off));
6769   a = &array[off];
6770   if (!cdof || setBC) {
6771     if (clperm) {
6772       if (perm) {
6773         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6774       } else {
6775         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6776       }
6777     } else {
6778       if (perm) {
6779         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6780       } else {
6781         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6782       }
6783     }
6784   } else {
6785     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6786     if (clperm) {
6787       if (perm) {
6788         for (k = 0; k < dof; ++k) {
6789           if ((cind < cdof) && (k == cdofs[cind])) {
6790             ++cind;
6791             continue;
6792           }
6793           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6794         }
6795       } else {
6796         for (k = 0; k < dof; ++k) {
6797           if ((cind < cdof) && (k == cdofs[cind])) {
6798             ++cind;
6799             continue;
6800           }
6801           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6802         }
6803       }
6804     } else {
6805       if (perm) {
6806         for (k = 0; k < dof; ++k) {
6807           if ((cind < cdof) && (k == cdofs[cind])) {
6808             ++cind;
6809             continue;
6810           }
6811           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6812         }
6813       } else {
6814         for (k = 0; k < dof; ++k) {
6815           if ((cind < cdof) && (k == cdofs[cind])) {
6816             ++cind;
6817             continue;
6818           }
6819           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6820         }
6821       }
6822     }
6823   }
6824   PetscFunctionReturn(PETSC_SUCCESS);
6825 }
6826 
6827 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[])
6828 {
6829   PetscInt        cdof;  /* The number of constraints on this point */
6830   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6831   PetscScalar    *a;
6832   PetscInt        off, cind = 0, k;
6833 
6834   PetscFunctionBegin;
6835   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6836   PetscCall(PetscSectionGetOffset(section, point, &off));
6837   a = &array[off];
6838   if (cdof) {
6839     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6840     if (clperm) {
6841       if (perm) {
6842         for (k = 0; k < dof; ++k) {
6843           if ((cind < cdof) && (k == cdofs[cind])) {
6844             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6845             cind++;
6846           }
6847         }
6848       } else {
6849         for (k = 0; k < dof; ++k) {
6850           if ((cind < cdof) && (k == cdofs[cind])) {
6851             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6852             cind++;
6853           }
6854         }
6855       }
6856     } else {
6857       if (perm) {
6858         for (k = 0; k < dof; ++k) {
6859           if ((cind < cdof) && (k == cdofs[cind])) {
6860             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6861             cind++;
6862           }
6863         }
6864       } else {
6865         for (k = 0; k < dof; ++k) {
6866           if ((cind < cdof) && (k == cdofs[cind])) {
6867             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6868             cind++;
6869           }
6870         }
6871       }
6872     }
6873   }
6874   PetscFunctionReturn(PETSC_SUCCESS);
6875 }
6876 
6877 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[])
6878 {
6879   PetscScalar    *a;
6880   PetscInt        fdof, foff, fcdof, foffset = *offset;
6881   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6882   PetscInt        cind = 0, b;
6883 
6884   PetscFunctionBegin;
6885   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6886   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6887   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6888   a = &array[foff];
6889   if (!fcdof || setBC) {
6890     if (clperm) {
6891       if (perm) {
6892         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6893       } else {
6894         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6895       }
6896     } else {
6897       if (perm) {
6898         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6899       } else {
6900         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6901       }
6902     }
6903   } else {
6904     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6905     if (clperm) {
6906       if (perm) {
6907         for (b = 0; b < fdof; b++) {
6908           if ((cind < fcdof) && (b == fcdofs[cind])) {
6909             ++cind;
6910             continue;
6911           }
6912           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6913         }
6914       } else {
6915         for (b = 0; b < fdof; b++) {
6916           if ((cind < fcdof) && (b == fcdofs[cind])) {
6917             ++cind;
6918             continue;
6919           }
6920           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6921         }
6922       }
6923     } else {
6924       if (perm) {
6925         for (b = 0; b < fdof; b++) {
6926           if ((cind < fcdof) && (b == fcdofs[cind])) {
6927             ++cind;
6928             continue;
6929           }
6930           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6931         }
6932       } else {
6933         for (b = 0; b < fdof; b++) {
6934           if ((cind < fcdof) && (b == fcdofs[cind])) {
6935             ++cind;
6936             continue;
6937           }
6938           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6939         }
6940       }
6941     }
6942   }
6943   *offset += fdof;
6944   PetscFunctionReturn(PETSC_SUCCESS);
6945 }
6946 
6947 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[])
6948 {
6949   PetscScalar    *a;
6950   PetscInt        fdof, foff, fcdof, foffset = *offset;
6951   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6952   PetscInt        Nc, cind = 0, ncind = 0, b;
6953   PetscBool       ncSet, fcSet;
6954 
6955   PetscFunctionBegin;
6956   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6957   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6958   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6959   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6960   a = &array[foff];
6961   if (fcdof) {
6962     /* We just override fcdof and fcdofs with Ncc and comps */
6963     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6964     if (clperm) {
6965       if (perm) {
6966         if (comps) {
6967           for (b = 0; b < fdof; b++) {
6968             ncSet = fcSet = PETSC_FALSE;
6969             if (b % Nc == comps[ncind]) {
6970               ncind = (ncind + 1) % Ncc;
6971               ncSet = PETSC_TRUE;
6972             }
6973             if ((cind < fcdof) && (b == fcdofs[cind])) {
6974               ++cind;
6975               fcSet = PETSC_TRUE;
6976             }
6977             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6978           }
6979         } else {
6980           for (b = 0; b < fdof; b++) {
6981             if ((cind < fcdof) && (b == fcdofs[cind])) {
6982               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6983               ++cind;
6984             }
6985           }
6986         }
6987       } else {
6988         if (comps) {
6989           for (b = 0; b < fdof; b++) {
6990             ncSet = fcSet = PETSC_FALSE;
6991             if (b % Nc == comps[ncind]) {
6992               ncind = (ncind + 1) % Ncc;
6993               ncSet = PETSC_TRUE;
6994             }
6995             if ((cind < fcdof) && (b == fcdofs[cind])) {
6996               ++cind;
6997               fcSet = PETSC_TRUE;
6998             }
6999             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
7000           }
7001         } else {
7002           for (b = 0; b < fdof; b++) {
7003             if ((cind < fcdof) && (b == fcdofs[cind])) {
7004               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
7005               ++cind;
7006             }
7007           }
7008         }
7009       }
7010     } else {
7011       if (perm) {
7012         if (comps) {
7013           for (b = 0; b < fdof; b++) {
7014             ncSet = fcSet = PETSC_FALSE;
7015             if (b % Nc == comps[ncind]) {
7016               ncind = (ncind + 1) % Ncc;
7017               ncSet = PETSC_TRUE;
7018             }
7019             if ((cind < fcdof) && (b == fcdofs[cind])) {
7020               ++cind;
7021               fcSet = PETSC_TRUE;
7022             }
7023             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
7024           }
7025         } else {
7026           for (b = 0; b < fdof; b++) {
7027             if ((cind < fcdof) && (b == fcdofs[cind])) {
7028               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
7029               ++cind;
7030             }
7031           }
7032         }
7033       } else {
7034         if (comps) {
7035           for (b = 0; b < fdof; b++) {
7036             ncSet = fcSet = PETSC_FALSE;
7037             if (b % Nc == comps[ncind]) {
7038               ncind = (ncind + 1) % Ncc;
7039               ncSet = PETSC_TRUE;
7040             }
7041             if ((cind < fcdof) && (b == fcdofs[cind])) {
7042               ++cind;
7043               fcSet = PETSC_TRUE;
7044             }
7045             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
7046           }
7047         } else {
7048           for (b = 0; b < fdof; b++) {
7049             if ((cind < fcdof) && (b == fcdofs[cind])) {
7050               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
7051               ++cind;
7052             }
7053           }
7054         }
7055       }
7056     }
7057   }
7058   *offset += fdof;
7059   PetscFunctionReturn(PETSC_SUCCESS);
7060 }
7061 
7062 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7063 {
7064   PetscScalar    *array;
7065   const PetscInt *cone, *coneO;
7066   PetscInt        pStart, pEnd, p, numPoints, off, dof;
7067 
7068   PetscFunctionBeginHot;
7069   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
7070   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
7071   PetscCall(DMPlexGetCone(dm, point, &cone));
7072   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
7073   PetscCall(VecGetArray(v, &array));
7074   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
7075     const PetscInt cp = !p ? point : cone[p - 1];
7076     const PetscInt o  = !p ? 0 : coneO[p - 1];
7077 
7078     if ((cp < pStart) || (cp >= pEnd)) {
7079       dof = 0;
7080       continue;
7081     }
7082     PetscCall(PetscSectionGetDof(section, cp, &dof));
7083     /* ADD_VALUES */
7084     {
7085       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7086       PetscScalar    *a;
7087       PetscInt        cdof, coff, cind = 0, k;
7088 
7089       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
7090       PetscCall(PetscSectionGetOffset(section, cp, &coff));
7091       a = &array[coff];
7092       if (!cdof) {
7093         if (o >= 0) {
7094           for (k = 0; k < dof; ++k) a[k] += values[off + k];
7095         } else {
7096           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
7097         }
7098       } else {
7099         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
7100         if (o >= 0) {
7101           for (k = 0; k < dof; ++k) {
7102             if ((cind < cdof) && (k == cdofs[cind])) {
7103               ++cind;
7104               continue;
7105             }
7106             a[k] += values[off + k];
7107           }
7108         } else {
7109           for (k = 0; k < dof; ++k) {
7110             if ((cind < cdof) && (k == cdofs[cind])) {
7111               ++cind;
7112               continue;
7113             }
7114             a[k] += values[off + dof - k - 1];
7115           }
7116         }
7117       }
7118     }
7119   }
7120   PetscCall(VecRestoreArray(v, &array));
7121   PetscFunctionReturn(PETSC_SUCCESS);
7122 }
7123 
7124 /*@C
7125   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
7126 
7127   Not collective
7128 
7129   Input Parameters:
7130 + dm      - The `DM`
7131 . section - The section describing the layout in `v`, or `NULL` to use the default section
7132 . v       - The local vector
7133 . point   - The point in the `DM`
7134 . values  - The array of values
7135 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
7136             where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
7137 
7138   Level: intermediate
7139 
7140   Note:
7141   Usually the input arrays were obtained with `DMPlexVecGetClosure()`
7142 
7143   Fortran Note:
7144   `values` must be declared with
7145 .vb
7146   PetscScalar,dimension(:),pointer   :: values
7147 .ve
7148 
7149 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
7150 @*/
7151 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7152 {
7153   PetscSection    clSection;
7154   IS              clPoints;
7155   PetscScalar    *array;
7156   PetscInt       *points = NULL;
7157   const PetscInt *clp, *clperm = NULL;
7158   PetscInt        depth, numFields, numPoints, p, clsize;
7159 
7160   PetscFunctionBeginHot;
7161   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7162   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7163   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7164   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7165   PetscCall(DMPlexGetDepth(dm, &depth));
7166   PetscCall(PetscSectionGetNumFields(section, &numFields));
7167   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
7168     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
7169     PetscFunctionReturn(PETSC_SUCCESS);
7170   }
7171   /* Get points */
7172   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7173   for (clsize = 0, p = 0; p < numPoints; p++) {
7174     PetscInt dof;
7175     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7176     clsize += dof;
7177   }
7178   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7179   /* Get array */
7180   PetscCall(VecGetArray(v, &array));
7181   /* Get values */
7182   if (numFields > 0) {
7183     PetscInt offset = 0, f;
7184     for (f = 0; f < numFields; ++f) {
7185       const PetscInt    **perms = NULL;
7186       const PetscScalar **flips = NULL;
7187 
7188       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7189       switch (mode) {
7190       case INSERT_VALUES:
7191         for (p = 0; p < numPoints; p++) {
7192           const PetscInt     point = points[2 * p];
7193           const PetscInt    *perm  = perms ? perms[p] : NULL;
7194           const PetscScalar *flip  = flips ? flips[p] : NULL;
7195           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
7196         }
7197         break;
7198       case INSERT_ALL_VALUES:
7199         for (p = 0; p < numPoints; p++) {
7200           const PetscInt     point = points[2 * p];
7201           const PetscInt    *perm  = perms ? perms[p] : NULL;
7202           const PetscScalar *flip  = flips ? flips[p] : NULL;
7203           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
7204         }
7205         break;
7206       case INSERT_BC_VALUES:
7207         for (p = 0; p < numPoints; p++) {
7208           const PetscInt     point = points[2 * p];
7209           const PetscInt    *perm  = perms ? perms[p] : NULL;
7210           const PetscScalar *flip  = flips ? flips[p] : NULL;
7211           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
7212         }
7213         break;
7214       case ADD_VALUES:
7215         for (p = 0; p < numPoints; p++) {
7216           const PetscInt     point = points[2 * p];
7217           const PetscInt    *perm  = perms ? perms[p] : NULL;
7218           const PetscScalar *flip  = flips ? flips[p] : NULL;
7219           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
7220         }
7221         break;
7222       case ADD_ALL_VALUES:
7223         for (p = 0; p < numPoints; p++) {
7224           const PetscInt     point = points[2 * p];
7225           const PetscInt    *perm  = perms ? perms[p] : NULL;
7226           const PetscScalar *flip  = flips ? flips[p] : NULL;
7227           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
7228         }
7229         break;
7230       case ADD_BC_VALUES:
7231         for (p = 0; p < numPoints; p++) {
7232           const PetscInt     point = points[2 * p];
7233           const PetscInt    *perm  = perms ? perms[p] : NULL;
7234           const PetscScalar *flip  = flips ? flips[p] : NULL;
7235           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
7236         }
7237         break;
7238       default:
7239         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7240       }
7241       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7242     }
7243   } else {
7244     PetscInt            dof, off;
7245     const PetscInt    **perms = NULL;
7246     const PetscScalar **flips = NULL;
7247 
7248     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
7249     switch (mode) {
7250     case INSERT_VALUES:
7251       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7252         const PetscInt     point = points[2 * p];
7253         const PetscInt    *perm  = perms ? perms[p] : NULL;
7254         const PetscScalar *flip  = flips ? flips[p] : NULL;
7255         PetscCall(PetscSectionGetDof(section, point, &dof));
7256         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
7257       }
7258       break;
7259     case INSERT_ALL_VALUES:
7260       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7261         const PetscInt     point = points[2 * p];
7262         const PetscInt    *perm  = perms ? perms[p] : NULL;
7263         const PetscScalar *flip  = flips ? flips[p] : NULL;
7264         PetscCall(PetscSectionGetDof(section, point, &dof));
7265         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
7266       }
7267       break;
7268     case INSERT_BC_VALUES:
7269       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7270         const PetscInt     point = points[2 * p];
7271         const PetscInt    *perm  = perms ? perms[p] : NULL;
7272         const PetscScalar *flip  = flips ? flips[p] : NULL;
7273         PetscCall(PetscSectionGetDof(section, point, &dof));
7274         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
7275       }
7276       break;
7277     case ADD_VALUES:
7278       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7279         const PetscInt     point = points[2 * p];
7280         const PetscInt    *perm  = perms ? perms[p] : NULL;
7281         const PetscScalar *flip  = flips ? flips[p] : NULL;
7282         PetscCall(PetscSectionGetDof(section, point, &dof));
7283         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
7284       }
7285       break;
7286     case ADD_ALL_VALUES:
7287       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7288         const PetscInt     point = points[2 * p];
7289         const PetscInt    *perm  = perms ? perms[p] : NULL;
7290         const PetscScalar *flip  = flips ? flips[p] : NULL;
7291         PetscCall(PetscSectionGetDof(section, point, &dof));
7292         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
7293       }
7294       break;
7295     case ADD_BC_VALUES:
7296       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7297         const PetscInt     point = points[2 * p];
7298         const PetscInt    *perm  = perms ? perms[p] : NULL;
7299         const PetscScalar *flip  = flips ? flips[p] : NULL;
7300         PetscCall(PetscSectionGetDof(section, point, &dof));
7301         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
7302       }
7303       break;
7304     default:
7305       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7306     }
7307     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
7308   }
7309   /* Cleanup points */
7310   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7311   /* Cleanup array */
7312   PetscCall(VecRestoreArray(v, &array));
7313   PetscFunctionReturn(PETSC_SUCCESS);
7314 }
7315 
7316 /* Check whether the given point is in the label. If not, update the offset to skip this point */
7317 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
7318 {
7319   PetscFunctionBegin;
7320   *contains = PETSC_TRUE;
7321   if (label) {
7322     PetscInt fdof;
7323 
7324     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
7325     if (!*contains) {
7326       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7327       *offset += fdof;
7328       PetscFunctionReturn(PETSC_SUCCESS);
7329     }
7330   }
7331   PetscFunctionReturn(PETSC_SUCCESS);
7332 }
7333 
7334 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
7335 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)
7336 {
7337   PetscSection    clSection;
7338   IS              clPoints;
7339   PetscScalar    *array;
7340   PetscInt       *points = NULL;
7341   const PetscInt *clp;
7342   PetscInt        numFields, numPoints, p;
7343   PetscInt        offset = 0, f;
7344 
7345   PetscFunctionBeginHot;
7346   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7347   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7348   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7349   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7350   PetscCall(PetscSectionGetNumFields(section, &numFields));
7351   /* Get points */
7352   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7353   /* Get array */
7354   PetscCall(VecGetArray(v, &array));
7355   /* Get values */
7356   for (f = 0; f < numFields; ++f) {
7357     const PetscInt    **perms = NULL;
7358     const PetscScalar **flips = NULL;
7359     PetscBool           contains;
7360 
7361     if (!fieldActive[f]) {
7362       for (p = 0; p < numPoints * 2; p += 2) {
7363         PetscInt fdof;
7364         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7365         offset += fdof;
7366       }
7367       continue;
7368     }
7369     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7370     switch (mode) {
7371     case INSERT_VALUES:
7372       for (p = 0; p < numPoints; p++) {
7373         const PetscInt     point = points[2 * p];
7374         const PetscInt    *perm  = perms ? perms[p] : NULL;
7375         const PetscScalar *flip  = flips ? flips[p] : NULL;
7376         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7377         if (!contains) continue;
7378         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
7379       }
7380       break;
7381     case INSERT_ALL_VALUES:
7382       for (p = 0; p < numPoints; p++) {
7383         const PetscInt     point = points[2 * p];
7384         const PetscInt    *perm  = perms ? perms[p] : NULL;
7385         const PetscScalar *flip  = flips ? flips[p] : NULL;
7386         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7387         if (!contains) continue;
7388         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
7389       }
7390       break;
7391     case INSERT_BC_VALUES:
7392       for (p = 0; p < numPoints; p++) {
7393         const PetscInt     point = points[2 * p];
7394         const PetscInt    *perm  = perms ? perms[p] : NULL;
7395         const PetscScalar *flip  = flips ? flips[p] : NULL;
7396         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7397         if (!contains) continue;
7398         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
7399       }
7400       break;
7401     case ADD_VALUES:
7402       for (p = 0; p < numPoints; p++) {
7403         const PetscInt     point = points[2 * p];
7404         const PetscInt    *perm  = perms ? perms[p] : NULL;
7405         const PetscScalar *flip  = flips ? flips[p] : NULL;
7406         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7407         if (!contains) continue;
7408         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
7409       }
7410       break;
7411     case ADD_ALL_VALUES:
7412       for (p = 0; p < numPoints; p++) {
7413         const PetscInt     point = points[2 * p];
7414         const PetscInt    *perm  = perms ? perms[p] : NULL;
7415         const PetscScalar *flip  = flips ? flips[p] : NULL;
7416         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7417         if (!contains) continue;
7418         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
7419       }
7420       break;
7421     default:
7422       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7423     }
7424     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7425   }
7426   /* Cleanup points */
7427   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7428   /* Cleanup array */
7429   PetscCall(VecRestoreArray(v, &array));
7430   PetscFunctionReturn(PETSC_SUCCESS);
7431 }
7432 
7433 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
7434 {
7435   PetscMPIInt rank;
7436   PetscInt    i, j;
7437 
7438   PetscFunctionBegin;
7439   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7440   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
7441   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
7442   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
7443   numCIndices = numCIndices ? numCIndices : numRIndices;
7444   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
7445   for (i = 0; i < numRIndices; i++) {
7446     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
7447     for (j = 0; j < numCIndices; j++) {
7448 #if defined(PETSC_USE_COMPLEX)
7449       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
7450 #else
7451       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
7452 #endif
7453     }
7454     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
7455   }
7456   PetscFunctionReturn(PETSC_SUCCESS);
7457 }
7458 
7459 /*
7460   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
7461 
7462   Input Parameters:
7463 + section - The section for this data layout
7464 . islocal - Is the section (and thus indices being requested) local or global?
7465 . point   - The point contributing dofs with these indices
7466 . off     - The global offset of this point
7467 . loff    - The local offset of each field
7468 . setBC   - The flag determining whether to include indices of boundary values
7469 . perm    - A permutation of the dofs on this point, or NULL
7470 - indperm - A permutation of the entire indices array, or NULL
7471 
7472   Output Parameter:
7473 . indices - Indices for dofs on this point
7474 
7475   Level: developer
7476 
7477   Note: The indices could be local or global, depending on the value of 'off'.
7478 */
7479 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7480 {
7481   PetscInt        dof;   /* The number of unknowns on this point */
7482   PetscInt        cdof;  /* The number of constraints on this point */
7483   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7484   PetscInt        cind = 0, k;
7485 
7486   PetscFunctionBegin;
7487   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7488   PetscCall(PetscSectionGetDof(section, point, &dof));
7489   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7490   if (!cdof || setBC) {
7491     for (k = 0; k < dof; ++k) {
7492       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7493       const PetscInt ind    = indperm ? indperm[preind] : preind;
7494 
7495       indices[ind] = off + k;
7496     }
7497   } else {
7498     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7499     for (k = 0; k < dof; ++k) {
7500       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7501       const PetscInt ind    = indperm ? indperm[preind] : preind;
7502 
7503       if ((cind < cdof) && (k == cdofs[cind])) {
7504         /* Insert check for returning constrained indices */
7505         indices[ind] = -(off + k + 1);
7506         ++cind;
7507       } else {
7508         indices[ind] = off + k - (islocal ? 0 : cind);
7509       }
7510     }
7511   }
7512   *loff += dof;
7513   PetscFunctionReturn(PETSC_SUCCESS);
7514 }
7515 
7516 /*
7517  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7518 
7519  Input Parameters:
7520 + section - a section (global or local)
7521 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7522 . point - point within section
7523 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7524 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7525 . setBC - identify constrained (boundary condition) points via involution.
7526 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7527 . permsoff - offset
7528 - indperm - index permutation
7529 
7530  Output Parameter:
7531 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7532 . indices - array to hold indices (as defined by section) of each dof associated with point
7533 
7534  Notes:
7535  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7536  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7537  in the local vector.
7538 
7539  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7540  significant).  It is invalid to call with a global section and setBC=true.
7541 
7542  Developer Note:
7543  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7544  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7545  offset could be obtained from the section instead of passing it explicitly as we do now.
7546 
7547  Example:
7548  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7549  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7550  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7551  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.
7552 
7553  Level: developer
7554 */
7555 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[])
7556 {
7557   PetscInt numFields, foff, f;
7558 
7559   PetscFunctionBegin;
7560   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7561   PetscCall(PetscSectionGetNumFields(section, &numFields));
7562   for (f = 0, foff = 0; f < numFields; ++f) {
7563     PetscInt        fdof, cfdof;
7564     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7565     PetscInt        cind = 0, b;
7566     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7567 
7568     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7569     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7570     if (!cfdof || setBC) {
7571       for (b = 0; b < fdof; ++b) {
7572         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7573         const PetscInt ind    = indperm ? indperm[preind] : preind;
7574 
7575         indices[ind] = off + foff + b;
7576       }
7577     } else {
7578       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7579       for (b = 0; b < fdof; ++b) {
7580         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7581         const PetscInt ind    = indperm ? indperm[preind] : preind;
7582 
7583         if ((cind < cfdof) && (b == fcdofs[cind])) {
7584           indices[ind] = -(off + foff + b + 1);
7585           ++cind;
7586         } else {
7587           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7588         }
7589       }
7590     }
7591     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7592     foffs[f] += fdof;
7593   }
7594   PetscFunctionReturn(PETSC_SUCCESS);
7595 }
7596 
7597 /*
7598   This version believes the globalSection offsets for each field, rather than just the point offset
7599 
7600  . foffs - The offset into 'indices' for each field, since it is segregated by field
7601 
7602  Notes:
7603  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7604  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7605 */
7606 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7607 {
7608   PetscInt numFields, foff, f;
7609 
7610   PetscFunctionBegin;
7611   PetscCall(PetscSectionGetNumFields(section, &numFields));
7612   for (f = 0; f < numFields; ++f) {
7613     PetscInt        fdof, cfdof;
7614     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7615     PetscInt        cind = 0, b;
7616     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7617 
7618     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7619     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7620     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7621     if (!cfdof) {
7622       for (b = 0; b < fdof; ++b) {
7623         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7624         const PetscInt ind    = indperm ? indperm[preind] : preind;
7625 
7626         indices[ind] = foff + b;
7627       }
7628     } else {
7629       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7630       for (b = 0; b < fdof; ++b) {
7631         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7632         const PetscInt ind    = indperm ? indperm[preind] : preind;
7633 
7634         if ((cind < cfdof) && (b == fcdofs[cind])) {
7635           indices[ind] = -(foff + b + 1);
7636           ++cind;
7637         } else {
7638           indices[ind] = foff + b - cind;
7639         }
7640       }
7641     }
7642     foffs[f] += fdof;
7643   }
7644   PetscFunctionReturn(PETSC_SUCCESS);
7645 }
7646 
7647 static PetscErrorCode DMPlexAnchorsGetSubMatIndices(PetscInt nPoints, const PetscInt pnts[], PetscSection section, PetscSection cSec, PetscInt tmpIndices[], PetscInt fieldOffsets[], PetscInt indices[], const PetscInt ***perms)
7648 {
7649   PetscInt numFields, sStart, sEnd, cStart, cEnd;
7650 
7651   PetscFunctionBegin;
7652   PetscCall(PetscSectionGetNumFields(section, &numFields));
7653   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7654   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7655   for (PetscInt p = 0; p < nPoints; p++) {
7656     PetscInt     b       = pnts[2 * p];
7657     PetscInt     bSecDof = 0, bOff;
7658     PetscInt     cSecDof = 0;
7659     PetscSection indices_section;
7660 
7661     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7662     if (!bSecDof) continue;
7663     if (b >= cStart && b < cEnd) PetscCall(PetscSectionGetDof(cSec, b, &cSecDof));
7664     indices_section = cSecDof > 0 ? cSec : section;
7665     if (numFields) {
7666       PetscInt fStart[32], fEnd[32];
7667 
7668       fStart[0] = 0;
7669       fEnd[0]   = 0;
7670       for (PetscInt f = 0; f < numFields; f++) {
7671         PetscInt fDof = 0;
7672 
7673         PetscCall(PetscSectionGetFieldDof(indices_section, b, f, &fDof));
7674         fStart[f + 1] = fStart[f] + fDof;
7675         fEnd[f + 1]   = fStart[f + 1];
7676       }
7677       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7678       // only apply permutations on one side
7679       PetscCall(DMPlexGetIndicesPointFields_Internal(indices_section, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, perms ? p : -1, NULL, tmpIndices));
7680       for (PetscInt f = 0; f < numFields; f++) {
7681         for (PetscInt i = fStart[f]; i < fEnd[f]; i++) { indices[fieldOffsets[f]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1); }
7682       }
7683     } else {
7684       PetscInt bEnd = 0;
7685 
7686       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7687       PetscCall(DMPlexGetIndicesPoint_Internal(indices_section, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, tmpIndices));
7688 
7689       for (PetscInt i = 0; i < bEnd; i++) indices[fieldOffsets[0]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1);
7690     }
7691   }
7692   PetscFunctionReturn(PETSC_SUCCESS);
7693 }
7694 
7695 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[])
7696 {
7697   Mat             cMat;
7698   PetscSection    aSec, cSec;
7699   IS              aIS;
7700   PetscInt        aStart = -1, aEnd = -1;
7701   PetscInt        sStart = -1, sEnd = -1;
7702   PetscInt        cStart = -1, cEnd = -1;
7703   const PetscInt *anchors;
7704   PetscInt        numFields, p;
7705   PetscInt        newNumPoints = 0, newNumIndices = 0;
7706   PetscInt       *newPoints, *indices, *newIndices, *tmpIndices, *tmpNewIndices;
7707   PetscInt        oldOffsets[32];
7708   PetscInt        newOffsets[32];
7709   PetscInt        oldOffsetsCopy[32];
7710   PetscInt        newOffsetsCopy[32];
7711   PetscScalar    *modMat         = NULL;
7712   PetscBool       anyConstrained = PETSC_FALSE;
7713 
7714   PetscFunctionBegin;
7715   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7716   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7717   PetscCall(PetscSectionGetNumFields(section, &numFields));
7718 
7719   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7720   /* if there are point-to-point constraints */
7721   if (aSec) {
7722     PetscCall(PetscArrayzero(newOffsets, 32));
7723     PetscCall(PetscArrayzero(oldOffsets, 32));
7724     PetscCall(ISGetIndices(aIS, &anchors));
7725     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7726     PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7727     /* figure out how many points are going to be in the new element matrix
7728      * (we allow double counting, because it's all just going to be summed
7729      * into the global matrix anyway) */
7730     for (p = 0; p < 2 * numPoints; p += 2) {
7731       PetscInt b    = points[p];
7732       PetscInt bDof = 0, bSecDof = 0;
7733 
7734       if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7735       if (!bSecDof) continue;
7736 
7737       for (PetscInt f = 0; f < numFields; f++) {
7738         PetscInt fDof = 0;
7739 
7740         PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7741         oldOffsets[f + 1] += fDof;
7742       }
7743       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7744       if (bDof) {
7745         /* this point is constrained */
7746         /* it is going to be replaced by its anchors */
7747         PetscInt bOff, q;
7748 
7749         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7750         for (q = 0; q < bDof; q++) {
7751           PetscInt a    = anchors[bOff + q];
7752           PetscInt aDof = 0;
7753 
7754           if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7755           if (aDof) {
7756             anyConstrained = PETSC_TRUE;
7757             newNumPoints += 1;
7758           }
7759           newNumIndices += aDof;
7760           for (PetscInt f = 0; f < numFields; ++f) {
7761             PetscInt fDof = 0;
7762 
7763             if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7764             newOffsets[f + 1] += fDof;
7765           }
7766         }
7767       } else {
7768         /* this point is not constrained */
7769         newNumPoints++;
7770         newNumIndices += bSecDof;
7771         for (PetscInt f = 0; f < numFields; ++f) {
7772           PetscInt fDof;
7773 
7774           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7775           newOffsets[f + 1] += fDof;
7776         }
7777       }
7778     }
7779   }
7780   if (!anyConstrained) {
7781     if (outNumPoints) *outNumPoints = 0;
7782     if (outNumIndices) *outNumIndices = 0;
7783     if (outPoints) *outPoints = NULL;
7784     if (outMat) *outMat = NULL;
7785     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7786     PetscFunctionReturn(PETSC_SUCCESS);
7787   }
7788 
7789   if (outNumPoints) *outNumPoints = newNumPoints;
7790   if (outNumIndices) *outNumIndices = newNumIndices;
7791 
7792   for (PetscInt f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7793   for (PetscInt f = 0; f < numFields; ++f) oldOffsets[f + 1] += oldOffsets[f];
7794 
7795   if (!outPoints && !outMat) {
7796     if (offsets) {
7797       for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7798     }
7799     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7800     PetscFunctionReturn(PETSC_SUCCESS);
7801   }
7802 
7803   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7804   PetscCheck(!numFields || oldOffsets[numFields] == numIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, oldOffsets[numFields], numIndices);
7805 
7806   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7807   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7808 
7809   /* output arrays */
7810   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7811   PetscCall(PetscArrayzero(newPoints, 2 * newNumPoints));
7812 
7813   // get the new Points
7814   for (PetscInt p = 0, newP = 0; p < numPoints; p++) {
7815     PetscInt b    = points[2 * p];
7816     PetscInt bDof = 0, bSecDof = 0, bOff;
7817 
7818     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7819     if (!bSecDof) continue;
7820     if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7821     if (bDof) {
7822       PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7823       for (PetscInt q = 0; q < bDof; q++) {
7824         PetscInt a = anchors[bOff + q], aDof = 0;
7825 
7826         if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7827         if (aDof) {
7828           newPoints[2 * newP]     = a;
7829           newPoints[2 * newP + 1] = 0; // orientations are accounted for in constructing the matrix, newly added points are in default orientation
7830           newP++;
7831         }
7832       }
7833     } else {
7834       newPoints[2 * newP]     = b;
7835       newPoints[2 * newP + 1] = points[2 * p + 1];
7836       newP++;
7837     }
7838   }
7839 
7840   if (outMat) {
7841     PetscScalar *tmpMat;
7842     PetscCall(PetscArraycpy(oldOffsetsCopy, oldOffsets, 32));
7843     PetscCall(PetscArraycpy(newOffsetsCopy, newOffsets, 32));
7844 
7845     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &indices));
7846     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7847     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7848     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7849 
7850     for (PetscInt i = 0; i < numIndices; i++) indices[i] = -1;
7851     for (PetscInt i = 0; i < newNumIndices; i++) newIndices[i] = -1;
7852 
7853     PetscCall(DMPlexAnchorsGetSubMatIndices(numPoints, points, section, cSec, tmpIndices, oldOffsetsCopy, indices, perms));
7854     PetscCall(DMPlexAnchorsGetSubMatIndices(newNumPoints, newPoints, section, section, tmpNewIndices, newOffsetsCopy, newIndices, NULL));
7855 
7856     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7857     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7858     PetscCall(PetscArrayzero(modMat, newNumIndices * numIndices));
7859     // for each field, insert the anchor modification into modMat
7860     for (PetscInt f = 0; f < PetscMax(1, numFields); f++) {
7861       PetscInt fStart    = oldOffsets[f];
7862       PetscInt fNewStart = newOffsets[f];
7863       for (PetscInt p = 0, newP = 0, o = fStart, oNew = fNewStart; p < numPoints; p++) {
7864         PetscInt b    = points[2 * p];
7865         PetscInt bDof = 0, bSecDof = 0, bOff;
7866 
7867         if (b >= sStart && b < sEnd) {
7868           if (numFields) {
7869             PetscCall(PetscSectionGetFieldDof(section, b, f, &bSecDof));
7870           } else {
7871             PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7872           }
7873         }
7874         if (!bSecDof) continue;
7875         if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7876         if (bDof) {
7877           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7878           for (PetscInt q = 0; q < bDof; q++, newP++) {
7879             PetscInt a = anchors[bOff + q], aDof = 0;
7880 
7881             if (a >= sStart && a < sEnd) {
7882               if (numFields) {
7883                 PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
7884               } else {
7885                 PetscCall(PetscSectionGetDof(section, a, &aDof));
7886               }
7887             }
7888             if (aDof) {
7889               PetscCall(MatGetValues(cMat, bSecDof, &indices[o], aDof, &newIndices[oNew], tmpMat));
7890               for (PetscInt d = 0; d < bSecDof; d++) {
7891                 for (PetscInt e = 0; e < aDof; e++) modMat[(o + d) * newNumIndices + oNew + e] = tmpMat[d * aDof + e];
7892               }
7893             }
7894             oNew += aDof;
7895           }
7896         } else {
7897           // Insert the identity matrix in this block
7898           for (PetscInt d = 0; d < bSecDof; d++) modMat[(o + d) * newNumIndices + oNew + d] = 1;
7899           oNew += bSecDof;
7900           newP++;
7901         }
7902         o += bSecDof;
7903       }
7904     }
7905 
7906     *outMat = modMat;
7907 
7908     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7909     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7910     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7911     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7912     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &indices));
7913   }
7914   PetscCall(ISRestoreIndices(aIS, &anchors));
7915 
7916   /* output */
7917   if (outPoints) {
7918     *outPoints = newPoints;
7919   } else {
7920     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7921   }
7922   for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7923   PetscFunctionReturn(PETSC_SUCCESS);
7924 }
7925 
7926 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)
7927 {
7928   PetscScalar *modMat        = NULL;
7929   PetscInt     newNumIndices = -1;
7930 
7931   PetscFunctionBegin;
7932   /* 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.
7933      modMat is that matrix C */
7934   PetscCall(DMPlexAnchorsGetSubMatModification(dm, section, numPoints, numIndices, points, perms, outNumPoints, &newNumIndices, outPoints, offsets, outValues ? &modMat : NULL));
7935   if (outNumIndices) *outNumIndices = newNumIndices;
7936   if (modMat) {
7937     const PetscScalar *newValues = values;
7938 
7939     if (multiplyRight) {
7940       PetscScalar *newNewValues = NULL;
7941       PetscBLASInt M, N, K;
7942       PetscScalar  a = 1.0, b = 0.0;
7943 
7944       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);
7945 
7946       PetscCall(PetscBLASIntCast(newNumIndices, &M));
7947       PetscCall(PetscBLASIntCast(numRows, &N));
7948       PetscCall(PetscBLASIntCast(numIndices, &K));
7949       PetscCall(DMGetWorkArray(dm, numRows * newNumIndices, MPIU_SCALAR, &newNewValues));
7950       // row-major to column-major conversion, right multiplication becomes left multiplication
7951       PetscCallBLAS("BLASgemm", BLASgemm_("N", "N", &M, &N, &K, &a, modMat, &M, newValues, &K, &b, newNewValues, &M));
7952       numCols   = newNumIndices;
7953       newValues = newNewValues;
7954     }
7955 
7956     if (multiplyLeft) {
7957       PetscScalar *newNewValues = NULL;
7958       PetscBLASInt M, N, K;
7959       PetscScalar  a = 1.0, b = 0.0;
7960 
7961       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);
7962 
7963       PetscCall(PetscBLASIntCast(numCols, &M));
7964       PetscCall(PetscBLASIntCast(newNumIndices, &N));
7965       PetscCall(PetscBLASIntCast(numIndices, &K));
7966       PetscCall(DMGetWorkArray(dm, newNumIndices * numCols, MPIU_SCALAR, &newNewValues));
7967       // row-major to column-major conversion, left multiplication becomes right multiplication
7968       PetscCallBLAS("BLASgemm", BLASgemm_("N", "T", &M, &N, &K, &a, newValues, &M, modMat, &N, &b, newNewValues, &M));
7969       if (newValues != values) PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &newValues));
7970       newValues = newNewValues;
7971     }
7972     *outValues = (PetscScalar *)newValues;
7973     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7974   }
7975   PetscFunctionReturn(PETSC_SUCCESS);
7976 }
7977 
7978 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)
7979 {
7980   PetscFunctionBegin;
7981   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, numPoints, numIndices, points, perms, numIndices, numIndices, values, outNumPoints, outNumIndices, outPoints, outValues, offsets, PETSC_TRUE, multiplyLeft));
7982   PetscFunctionReturn(PETSC_SUCCESS);
7983 }
7984 
7985 static PetscErrorCode DMPlexGetClosureIndicesSize_Internal(DM dm, PetscSection section, PetscInt point, PetscInt *closureSize)
7986 {
7987   /* Closure ordering */
7988   PetscSection    clSection;
7989   IS              clPoints;
7990   const PetscInt *clp;
7991   PetscInt       *points;
7992   PetscInt        Ncl, Ni = 0;
7993 
7994   PetscFunctionBeginHot;
7995   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7996   for (PetscInt p = 0; p < Ncl * 2; p += 2) {
7997     PetscInt dof;
7998 
7999     PetscCall(PetscSectionGetDof(section, points[p], &dof));
8000     Ni += dof;
8001   }
8002   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8003   *closureSize = Ni;
8004   PetscFunctionReturn(PETSC_SUCCESS);
8005 }
8006 
8007 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)
8008 {
8009   /* Closure ordering */
8010   PetscSection    clSection;
8011   IS              clPoints;
8012   const PetscInt *clp;
8013   PetscInt       *points;
8014   const PetscInt *clperm = NULL;
8015   /* Dof permutation and sign flips */
8016   const PetscInt    **perms[32] = {NULL};
8017   const PetscScalar **flips[32] = {NULL};
8018   PetscScalar        *valCopy   = NULL;
8019   /* Hanging node constraints */
8020   PetscInt    *pointsC = NULL;
8021   PetscScalar *valuesC = NULL;
8022   PetscInt     NclC, NiC;
8023 
8024   PetscInt *idx;
8025   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
8026   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
8027   PetscInt  idxStart, idxEnd;
8028   PetscInt  nRows, nCols;
8029 
8030   PetscFunctionBeginHot;
8031   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8032   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8033   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
8034   PetscAssertPointer(numRows, 6);
8035   PetscAssertPointer(numCols, 7);
8036   if (indices) PetscAssertPointer(indices, 8);
8037   if (outOffsets) PetscAssertPointer(outOffsets, 9);
8038   if (values) PetscAssertPointer(values, 10);
8039   PetscCall(PetscSectionGetNumFields(section, &Nf));
8040   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
8041   PetscCall(PetscArrayzero(offsets, 32));
8042   /* 1) Get points in closure */
8043   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
8044   if (useClPerm) {
8045     PetscInt depth, clsize;
8046     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
8047     for (clsize = 0, p = 0; p < Ncl; p++) {
8048       PetscInt dof;
8049       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
8050       clsize += dof;
8051     }
8052     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
8053   }
8054   /* 2) Get number of indices on these points and field offsets from section */
8055   for (p = 0; p < Ncl * 2; p += 2) {
8056     PetscInt dof, fdof;
8057 
8058     PetscCall(PetscSectionGetDof(section, points[p], &dof));
8059     for (f = 0; f < Nf; ++f) {
8060       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
8061       offsets[f + 1] += fdof;
8062     }
8063     Ni += dof;
8064   }
8065   if (*numRows == -1) *numRows = Ni;
8066   if (*numCols == -1) *numCols = Ni;
8067   nRows = *numRows;
8068   nCols = *numCols;
8069   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
8070   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
8071   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
8072   if (multiplyRight) PetscCheck(nCols == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " columns, got %" PetscInt_FMT, Ni, nCols);
8073   if (multiplyLeft) PetscCheck(nRows == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " rows, got %" PetscInt_FMT, Ni, nRows);
8074   for (f = 0; f < PetscMax(1, Nf); ++f) {
8075     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8076     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
8077     /* may need to apply sign changes to the element matrix */
8078     if (values && flips[f]) {
8079       PetscInt foffset = offsets[f];
8080 
8081       for (p = 0; p < Ncl; ++p) {
8082         PetscInt           pnt  = points[2 * p], fdof;
8083         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
8084 
8085         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
8086         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
8087         if (flip) {
8088           PetscInt i, j, k;
8089 
8090           if (!valCopy) {
8091             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8092             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
8093             *values = valCopy;
8094           }
8095           for (i = 0; i < fdof; ++i) {
8096             PetscScalar fval = flip[i];
8097 
8098             if (multiplyRight) {
8099               for (k = 0; k < nRows; ++k) { valCopy[Ni * k + (foffset + i)] *= fval; }
8100             }
8101             if (multiplyLeft) {
8102               for (k = 0; k < nCols; ++k) { valCopy[nCols * (foffset + i) + k] *= fval; }
8103             }
8104           }
8105         }
8106         foffset += fdof;
8107       }
8108     }
8109   }
8110   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
8111   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, Ncl, Ni, points, perms, nRows, nCols, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, multiplyRight, multiplyLeft));
8112   if (NclC) {
8113     if (multiplyRight) *numCols = NiC;
8114     if (multiplyLeft) *numRows = NiC;
8115     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8116     for (f = 0; f < PetscMax(1, Nf); ++f) {
8117       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8118       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8119     }
8120     for (f = 0; f < PetscMax(1, Nf); ++f) {
8121       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
8122       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
8123     }
8124     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8125     Ncl    = NclC;
8126     Ni     = NiC;
8127     points = pointsC;
8128     if (values) *values = valuesC;
8129   }
8130   /* 5) Calculate indices */
8131   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
8132   PetscCall(PetscSectionGetChart(idxSection, &idxStart, &idxEnd));
8133   if (Nf) {
8134     PetscInt  idxOff;
8135     PetscBool useFieldOffsets;
8136 
8137     if (outOffsets) {
8138       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
8139     }
8140     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
8141     if (useFieldOffsets) {
8142       for (p = 0; p < Ncl; ++p) {
8143         const PetscInt pnt = points[p * 2];
8144 
8145         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
8146       }
8147     } else {
8148       for (p = 0; p < Ncl; ++p) {
8149         const PetscInt pnt = points[p * 2];
8150 
8151         if (pnt < idxStart || pnt >= idxEnd) continue;
8152         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8153         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8154          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
8155          * global section. */
8156         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
8157       }
8158     }
8159   } else {
8160     PetscInt off = 0, idxOff;
8161 
8162     for (p = 0; p < Ncl; ++p) {
8163       const PetscInt  pnt  = points[p * 2];
8164       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
8165 
8166       if (pnt < idxStart || pnt >= idxEnd) continue;
8167       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8168       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8169        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
8170       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
8171     }
8172   }
8173   /* 6) Cleanup */
8174   for (f = 0; f < PetscMax(1, Nf); ++f) {
8175     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8176     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8177   }
8178   if (NclC) {
8179     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
8180   } else {
8181     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8182   }
8183 
8184   if (indices) *indices = idx;
8185   PetscFunctionReturn(PETSC_SUCCESS);
8186 }
8187 
8188 /*@C
8189   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
8190 
8191   Not collective
8192 
8193   Input Parameters:
8194 + dm         - The `DM`
8195 . section    - The `PetscSection` describing the points (a local section)
8196 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8197 . point      - The point defining the closure
8198 - useClPerm  - Use the closure point permutation if available
8199 
8200   Output Parameters:
8201 + numIndices - The number of dof indices in the closure of point with the input sections
8202 . indices    - The dof indices
8203 . outOffsets - Array to write the field offsets into, or `NULL`
8204 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8205 
8206   Level: advanced
8207 
8208   Notes:
8209   Call `DMPlexRestoreClosureIndices()` to free allocated memory
8210 
8211   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8212   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
8213   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8214   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
8215   indices (with the above semantics) are implied.
8216 
8217 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
8218           `PetscSection`, `DMGetGlobalSection()`
8219 @*/
8220 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8221 {
8222   PetscInt numRows = -1, numCols = -1;
8223 
8224   PetscFunctionBeginHot;
8225   PetscCall(DMPlexGetClosureIndices_Internal(dm, section, idxSection, point, useClPerm, &numRows, &numCols, indices, outOffsets, values, PETSC_TRUE, PETSC_TRUE));
8226   PetscCheck(numRows == numCols, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Symmetric matrix transformation produces rectangular dimensions (%" PetscInt_FMT ", %" PetscInt_FMT ")", numRows, numCols);
8227   *numIndices = numRows;
8228   PetscFunctionReturn(PETSC_SUCCESS);
8229 }
8230 
8231 /*@C
8232   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
8233 
8234   Not collective
8235 
8236   Input Parameters:
8237 + dm         - The `DM`
8238 . section    - The `PetscSection` describing the points (a local section)
8239 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8240 . point      - The point defining the closure
8241 - useClPerm  - Use the closure point permutation if available
8242 
8243   Output Parameters:
8244 + numIndices - The number of dof indices in the closure of point with the input sections
8245 . indices    - The dof indices
8246 . outOffsets - Array to write the field offsets into, or `NULL`
8247 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8248 
8249   Level: advanced
8250 
8251   Notes:
8252   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
8253 
8254   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8255   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
8256   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8257   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
8258   indices (with the above semantics) are implied.
8259 
8260 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
8261 @*/
8262 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8263 {
8264   PetscFunctionBegin;
8265   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8266   PetscAssertPointer(indices, 7);
8267   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
8268   PetscFunctionReturn(PETSC_SUCCESS);
8269 }
8270 
8271 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8272 {
8273   DM_Plex           *mesh = (DM_Plex *)dm->data;
8274   PetscInt          *indices;
8275   PetscInt           numIndices;
8276   const PetscScalar *valuesOrig = values;
8277   PetscErrorCode     ierr;
8278 
8279   PetscFunctionBegin;
8280   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8281   if (!section) PetscCall(DMGetLocalSection(dm, &section));
8282   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8283   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
8284   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
8285   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
8286 
8287   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
8288 
8289   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
8290   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8291   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
8292   if (ierr) {
8293     PetscMPIInt rank;
8294 
8295     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8296     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8297     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
8298     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8299     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8300     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
8301   }
8302   if (mesh->printFEM > 1) {
8303     PetscInt i;
8304     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
8305     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
8306     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8307   }
8308 
8309   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8310   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8311   PetscFunctionReturn(PETSC_SUCCESS);
8312 }
8313 
8314 /*@C
8315   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
8316 
8317   Not collective
8318 
8319   Input Parameters:
8320 + dm            - The `DM`
8321 . section       - The section describing the layout in `v`, or `NULL` to use the default section
8322 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
8323 . A             - The matrix
8324 . point         - The point in the `DM`
8325 . values        - The array of values
8326 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8327 
8328   Level: intermediate
8329 
8330 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8331 @*/
8332 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8333 {
8334   PetscFunctionBegin;
8335   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
8336   PetscFunctionReturn(PETSC_SUCCESS);
8337 }
8338 
8339 /*@C
8340   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
8341 
8342   Not collective
8343 
8344   Input Parameters:
8345 + dmRow            - The `DM` for the row fields
8346 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
8347 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
8348 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
8349 . dmCol            - The `DM` for the column fields
8350 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
8351 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
8352 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
8353 . A                - The matrix
8354 . point            - The point in the `DM`
8355 . values           - The array of values
8356 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8357 
8358   Level: intermediate
8359 
8360 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8361 @*/
8362 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)
8363 {
8364   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
8365   PetscInt          *indicesRow, *indicesCol;
8366   PetscInt           numIndicesRow = -1, numIndicesCol = -1;
8367   const PetscScalar *valuesV0 = values, *valuesV1, *valuesV2;
8368 
8369   PetscErrorCode ierr;
8370 
8371   PetscFunctionBegin;
8372   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
8373   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
8374   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
8375   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
8376   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
8377   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
8378   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
8379   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
8380   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
8381   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
8382   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
8383 
8384   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmRow, sectionRow, point, &numIndicesRow));
8385   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmCol, sectionCol, point, &numIndicesCol));
8386   valuesV1 = valuesV0;
8387   PetscCall(DMPlexGetClosureIndices_Internal(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV1, PETSC_FALSE, PETSC_TRUE));
8388   valuesV2 = valuesV1;
8389   PetscCall(DMPlexGetClosureIndices_Internal(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesRow, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2, PETSC_TRUE, PETSC_FALSE));
8390 
8391   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2));
8392   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8393   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2, mode);
8394   if (ierr) {
8395     PetscMPIInt rank;
8396 
8397     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8398     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8399     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8400     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV2));
8401     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8402     if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8403     if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8404   }
8405 
8406   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2));
8407   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8408   if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8409   if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8410   PetscFunctionReturn(PETSC_SUCCESS);
8411 }
8412 
8413 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8414 {
8415   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8416   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8417   PetscInt       *cpoints = NULL;
8418   PetscInt       *findices, *cindices;
8419   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8420   PetscInt        foffsets[32], coffsets[32];
8421   DMPolytopeType  ct;
8422   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8423   PetscErrorCode  ierr;
8424 
8425   PetscFunctionBegin;
8426   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8427   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8428   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8429   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8430   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8431   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8432   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8433   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8434   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8435   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8436   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8437   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8438   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8439   PetscCall(PetscArrayzero(foffsets, 32));
8440   PetscCall(PetscArrayzero(coffsets, 32));
8441   /* Column indices */
8442   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8443   maxFPoints = numCPoints;
8444   /* Compress out points not in the section */
8445   /*   TODO: Squeeze out points with 0 dof as well */
8446   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8447   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8448     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8449       cpoints[q * 2]     = cpoints[p];
8450       cpoints[q * 2 + 1] = cpoints[p + 1];
8451       ++q;
8452     }
8453   }
8454   numCPoints = q;
8455   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8456     PetscInt fdof;
8457 
8458     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8459     if (!dof) continue;
8460     for (f = 0; f < numFields; ++f) {
8461       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8462       coffsets[f + 1] += fdof;
8463     }
8464     numCIndices += dof;
8465   }
8466   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8467   /* Row indices */
8468   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8469   {
8470     DMPlexTransform tr;
8471     DMPolytopeType *rct;
8472     PetscInt       *rsize, *rcone, *rornt, Nt;
8473 
8474     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8475     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8476     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8477     numSubcells = rsize[Nt - 1];
8478     PetscCall(DMPlexTransformDestroy(&tr));
8479   }
8480   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8481   for (r = 0, q = 0; r < numSubcells; ++r) {
8482     /* TODO Map from coarse to fine cells */
8483     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8484     /* Compress out points not in the section */
8485     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8486     for (p = 0; p < numFPoints * 2; p += 2) {
8487       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8488         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8489         if (!dof) continue;
8490         for (s = 0; s < q; ++s)
8491           if (fpoints[p] == ftotpoints[s * 2]) break;
8492         if (s < q) continue;
8493         ftotpoints[q * 2]     = fpoints[p];
8494         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8495         ++q;
8496       }
8497     }
8498     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8499   }
8500   numFPoints = q;
8501   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8502     PetscInt fdof;
8503 
8504     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8505     if (!dof) continue;
8506     for (f = 0; f < numFields; ++f) {
8507       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8508       foffsets[f + 1] += fdof;
8509     }
8510     numFIndices += dof;
8511   }
8512   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8513 
8514   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8515   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8516   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8517   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8518   if (numFields) {
8519     const PetscInt **permsF[32] = {NULL};
8520     const PetscInt **permsC[32] = {NULL};
8521 
8522     for (f = 0; f < numFields; f++) {
8523       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8524       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8525     }
8526     for (p = 0; p < numFPoints; p++) {
8527       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8528       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8529     }
8530     for (p = 0; p < numCPoints; p++) {
8531       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8532       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8533     }
8534     for (f = 0; f < numFields; f++) {
8535       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8536       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8537     }
8538   } else {
8539     const PetscInt **permsF = NULL;
8540     const PetscInt **permsC = NULL;
8541 
8542     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8543     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8544     for (p = 0, off = 0; p < numFPoints; p++) {
8545       const PetscInt *perm = permsF ? permsF[p] : NULL;
8546 
8547       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8548       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8549     }
8550     for (p = 0, off = 0; p < numCPoints; p++) {
8551       const PetscInt *perm = permsC ? permsC[p] : NULL;
8552 
8553       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8554       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8555     }
8556     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8557     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8558   }
8559   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8560   /* TODO: flips */
8561   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8562   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8563   if (ierr) {
8564     PetscMPIInt rank;
8565 
8566     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8567     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8568     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8569     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8570     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8571   }
8572   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8573   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8574   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8575   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8576   PetscFunctionReturn(PETSC_SUCCESS);
8577 }
8578 
8579 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8580 {
8581   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8582   PetscInt       *cpoints      = NULL;
8583   PetscInt        foffsets[32] = {0}, coffsets[32] = {0};
8584   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8585   DMPolytopeType  ct;
8586   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8587 
8588   PetscFunctionBegin;
8589   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8590   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8591   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8592   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8593   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8594   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8595   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8596   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8597   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8598   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8599   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8600   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8601   /* Column indices */
8602   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8603   maxFPoints = numCPoints;
8604   /* Compress out points not in the section */
8605   /*   TODO: Squeeze out points with 0 dof as well */
8606   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8607   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8608     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8609       cpoints[q * 2]     = cpoints[p];
8610       cpoints[q * 2 + 1] = cpoints[p + 1];
8611       ++q;
8612     }
8613   }
8614   numCPoints = q;
8615   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8616     PetscInt fdof;
8617 
8618     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8619     if (!dof) continue;
8620     for (f = 0; f < numFields; ++f) {
8621       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8622       coffsets[f + 1] += fdof;
8623     }
8624     numCIndices += dof;
8625   }
8626   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8627   /* Row indices */
8628   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8629   {
8630     DMPlexTransform tr;
8631     DMPolytopeType *rct;
8632     PetscInt       *rsize, *rcone, *rornt, Nt;
8633 
8634     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8635     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8636     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8637     numSubcells = rsize[Nt - 1];
8638     PetscCall(DMPlexTransformDestroy(&tr));
8639   }
8640   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8641   for (r = 0, q = 0; r < numSubcells; ++r) {
8642     /* TODO Map from coarse to fine cells */
8643     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8644     /* Compress out points not in the section */
8645     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8646     for (p = 0; p < numFPoints * 2; p += 2) {
8647       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8648         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8649         if (!dof) continue;
8650         for (s = 0; s < q; ++s)
8651           if (fpoints[p] == ftotpoints[s * 2]) break;
8652         if (s < q) continue;
8653         ftotpoints[q * 2]     = fpoints[p];
8654         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8655         ++q;
8656       }
8657     }
8658     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8659   }
8660   numFPoints = q;
8661   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8662     PetscInt fdof;
8663 
8664     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8665     if (!dof) continue;
8666     for (f = 0; f < numFields; ++f) {
8667       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8668       foffsets[f + 1] += fdof;
8669     }
8670     numFIndices += dof;
8671   }
8672   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8673 
8674   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8675   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8676   if (numFields) {
8677     const PetscInt **permsF[32] = {NULL};
8678     const PetscInt **permsC[32] = {NULL};
8679 
8680     for (f = 0; f < numFields; f++) {
8681       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8682       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8683     }
8684     for (p = 0; p < numFPoints; p++) {
8685       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8686       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8687     }
8688     for (p = 0; p < numCPoints; p++) {
8689       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8690       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8691     }
8692     for (f = 0; f < numFields; f++) {
8693       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8694       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8695     }
8696   } else {
8697     const PetscInt **permsF = NULL;
8698     const PetscInt **permsC = NULL;
8699 
8700     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8701     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8702     for (p = 0, off = 0; p < numFPoints; p++) {
8703       const PetscInt *perm = permsF ? permsF[p] : NULL;
8704 
8705       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8706       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8707     }
8708     for (p = 0, off = 0; p < numCPoints; p++) {
8709       const PetscInt *perm = permsC ? permsC[p] : NULL;
8710 
8711       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8712       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8713     }
8714     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8715     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8716   }
8717   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8718   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8719   PetscFunctionReturn(PETSC_SUCCESS);
8720 }
8721 
8722 /*@
8723   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8724 
8725   Input Parameter:
8726 . dm - The `DMPLEX` object
8727 
8728   Output Parameter:
8729 . cellHeight - The height of a cell
8730 
8731   Level: developer
8732 
8733 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8734 @*/
8735 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8736 {
8737   DM_Plex *mesh = (DM_Plex *)dm->data;
8738 
8739   PetscFunctionBegin;
8740   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8741   PetscAssertPointer(cellHeight, 2);
8742   *cellHeight = mesh->vtkCellHeight;
8743   PetscFunctionReturn(PETSC_SUCCESS);
8744 }
8745 
8746 /*@
8747   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8748 
8749   Input Parameters:
8750 + dm         - The `DMPLEX` object
8751 - cellHeight - The height of a cell
8752 
8753   Level: developer
8754 
8755 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8756 @*/
8757 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8758 {
8759   DM_Plex *mesh = (DM_Plex *)dm->data;
8760 
8761   PetscFunctionBegin;
8762   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8763   mesh->vtkCellHeight = cellHeight;
8764   PetscFunctionReturn(PETSC_SUCCESS);
8765 }
8766 
8767 /*@
8768   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8769 
8770   Input Parameters:
8771 + dm - The `DMPLEX` object
8772 - ct - The `DMPolytopeType` of the cell
8773 
8774   Output Parameters:
8775 + start - The first cell of this type, or `NULL`
8776 - end   - The upper bound on this celltype, or `NULL`
8777 
8778   Level: advanced
8779 
8780 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8781 @*/
8782 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PetscInt *start, PetscInt *end)
8783 {
8784   DM_Plex *mesh = (DM_Plex *)dm->data;
8785   DMLabel  label;
8786   PetscInt pStart, pEnd;
8787 
8788   PetscFunctionBegin;
8789   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8790   if (start) {
8791     PetscAssertPointer(start, 3);
8792     *start = 0;
8793   }
8794   if (end) {
8795     PetscAssertPointer(end, 4);
8796     *end = 0;
8797   }
8798   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8799   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8800   if (mesh->tr) {
8801     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8802   } else {
8803     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8804     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8805     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8806   }
8807   PetscFunctionReturn(PETSC_SUCCESS);
8808 }
8809 
8810 /*@
8811   DMPlexGetDepthStratumGlobalSize - Get the global size for a given depth stratum
8812 
8813   Input Parameters:
8814 + dm    - The `DMPLEX` object
8815 - depth - The depth for the given point stratum
8816 
8817   Output Parameter:
8818 . gsize - The global number of points in the stratum
8819 
8820   Level: advanced
8821 
8822 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8823 @*/
8824 PetscErrorCode DMPlexGetDepthStratumGlobalSize(DM dm, PetscInt depth, PetscInt *gsize)
8825 {
8826   PetscSF         sf;
8827   const PetscInt *leaves;
8828   PetscInt        Nl, loc, start, end, lsize = 0;
8829 
8830   PetscFunctionBegin;
8831   PetscCall(DMGetPointSF(dm, &sf));
8832   PetscCall(PetscSFGetGraph(sf, NULL, &Nl, &leaves, NULL));
8833   PetscCall(DMPlexGetDepthStratum(dm, depth, &start, &end));
8834   for (PetscInt p = start; p < end; ++p) {
8835     PetscCall(PetscFindInt(p, Nl, leaves, &loc));
8836     if (loc < 0) ++lsize;
8837   }
8838   PetscCallMPI(MPI_Allreduce(&lsize, gsize, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
8839   PetscFunctionReturn(PETSC_SUCCESS);
8840 }
8841 
8842 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8843 {
8844   PetscSection section, globalSection;
8845   PetscInt    *numbers, p;
8846 
8847   PetscFunctionBegin;
8848   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8849   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8850   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8851   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8852   PetscCall(PetscSectionSetUp(section));
8853   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &globalSection));
8854   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8855   for (p = pStart; p < pEnd; ++p) {
8856     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8857     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8858     else numbers[p - pStart] += shift;
8859   }
8860   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8861   if (globalSize) {
8862     PetscLayout layout;
8863     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8864     PetscCall(PetscLayoutGetSize(layout, globalSize));
8865     PetscCall(PetscLayoutDestroy(&layout));
8866   }
8867   PetscCall(PetscSectionDestroy(&section));
8868   PetscCall(PetscSectionDestroy(&globalSection));
8869   PetscFunctionReturn(PETSC_SUCCESS);
8870 }
8871 
8872 /*@
8873   DMPlexCreateCellNumbering - Get a global cell numbering for all cells on this process
8874 
8875   Input Parameters:
8876 + dm         - The `DMPLEX` object
8877 - includeAll - Whether to include all cells, or just the simplex and box cells
8878 
8879   Output Parameter:
8880 . globalCellNumbers - Global cell numbers for all cells on this process
8881 
8882   Level: developer
8883 
8884 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`
8885 @*/
8886 PetscErrorCode DMPlexCreateCellNumbering(DM dm, PetscBool includeAll, IS *globalCellNumbers)
8887 {
8888   PetscInt cellHeight, cStart, cEnd;
8889 
8890   PetscFunctionBegin;
8891   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8892   if (includeAll) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8893   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8894   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8895   PetscFunctionReturn(PETSC_SUCCESS);
8896 }
8897 
8898 /*@
8899   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8900 
8901   Input Parameter:
8902 . dm - The `DMPLEX` object
8903 
8904   Output Parameter:
8905 . globalCellNumbers - Global cell numbers for all cells on this process
8906 
8907   Level: developer
8908 
8909 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateCellNumbering()`, `DMPlexGetVertexNumbering()`
8910 @*/
8911 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8912 {
8913   DM_Plex *mesh = (DM_Plex *)dm->data;
8914 
8915   PetscFunctionBegin;
8916   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8917   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8918   *globalCellNumbers = mesh->globalCellNumbers;
8919   PetscFunctionReturn(PETSC_SUCCESS);
8920 }
8921 
8922 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8923 {
8924   PetscInt vStart, vEnd;
8925 
8926   PetscFunctionBegin;
8927   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8928   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8929   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8930   PetscFunctionReturn(PETSC_SUCCESS);
8931 }
8932 
8933 /*@
8934   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8935 
8936   Input Parameter:
8937 . dm - The `DMPLEX` object
8938 
8939   Output Parameter:
8940 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8941 
8942   Level: developer
8943 
8944 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8945 @*/
8946 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8947 {
8948   DM_Plex *mesh = (DM_Plex *)dm->data;
8949 
8950   PetscFunctionBegin;
8951   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8952   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8953   *globalVertexNumbers = mesh->globalVertexNumbers;
8954   PetscFunctionReturn(PETSC_SUCCESS);
8955 }
8956 
8957 /*@
8958   DMPlexCreatePointNumbering - Create a global numbering for all points.
8959 
8960   Collective
8961 
8962   Input Parameter:
8963 . dm - The `DMPLEX` object
8964 
8965   Output Parameter:
8966 . globalPointNumbers - Global numbers for all points on this process
8967 
8968   Level: developer
8969 
8970   Notes:
8971   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8972   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8973   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8974   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8975 
8976   The partitioned mesh is
8977   ```
8978   (2)--0--(3)--1--(4)    (1)--0--(2)
8979   ```
8980   and its global numbering is
8981   ```
8982   (3)--0--(4)--1--(5)--2--(6)
8983   ```
8984   Then the global numbering is provided as
8985   ```
8986   [0] Number of indices in set 5
8987   [0] 0 0
8988   [0] 1 1
8989   [0] 2 3
8990   [0] 3 4
8991   [0] 4 -6
8992   [1] Number of indices in set 3
8993   [1] 0 2
8994   [1] 1 5
8995   [1] 2 6
8996   ```
8997 
8998 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8999 @*/
9000 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
9001 {
9002   IS          nums[4];
9003   PetscInt    depths[4], gdepths[4], starts[4];
9004   PetscInt    depth, d, shift = 0;
9005   PetscBool   empty = PETSC_FALSE;
9006   PetscMPIInt idepth;
9007 
9008   PetscFunctionBegin;
9009   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9010   PetscCall(DMPlexGetDepth(dm, &depth));
9011   // For unstratified meshes use dim instead of depth
9012   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
9013   // If any stratum is empty, we must mark all empty
9014   for (d = 0; d <= depth; ++d) {
9015     PetscInt end;
9016 
9017     depths[d] = depth - d;
9018     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
9019     if (!(starts[d] - end)) empty = PETSC_TRUE;
9020   }
9021   if (empty)
9022     for (d = 0; d <= depth; ++d) {
9023       depths[d] = -1;
9024       starts[d] = -1;
9025     }
9026   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
9027   PetscCall(PetscMPIIntCast(depth + 1, &idepth));
9028   PetscCallMPI(MPIU_Allreduce(depths, gdepths, idepth, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
9029   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]);
9030   // Note here that 'shift' is collective, so that the numbering is stratified by depth
9031   for (d = 0; d <= depth; ++d) {
9032     PetscInt pStart, pEnd, gsize;
9033 
9034     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
9035     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
9036     shift += gsize;
9037   }
9038   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
9039   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
9040   PetscFunctionReturn(PETSC_SUCCESS);
9041 }
9042 
9043 /*@
9044   DMPlexCreateEdgeNumbering - Create a global numbering for edges.
9045 
9046   Collective
9047 
9048   Input Parameter:
9049 . dm - The `DMPLEX` object
9050 
9051   Output Parameter:
9052 . globalEdgeNumbers - Global numbers for all edges on this process
9053 
9054   Level: developer
9055 
9056   Notes:
9057   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).
9058 
9059 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`, `DMPlexCreatePointNumbering()`
9060 @*/
9061 PetscErrorCode DMPlexCreateEdgeNumbering(DM dm, IS *globalEdgeNumbers)
9062 {
9063   PetscSF  sf;
9064   PetscInt eStart, eEnd;
9065 
9066   PetscFunctionBegin;
9067   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9068   PetscCall(DMGetPointSF(dm, &sf));
9069   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9070   PetscCall(DMPlexCreateNumbering_Plex(dm, eStart, eEnd, 0, NULL, sf, globalEdgeNumbers));
9071   PetscFunctionReturn(PETSC_SUCCESS);
9072 }
9073 
9074 /*@
9075   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
9076 
9077   Input Parameter:
9078 . dm - The `DMPLEX` object
9079 
9080   Output Parameter:
9081 . ranks - The rank field
9082 
9083   Options Database Key:
9084 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
9085 
9086   Level: intermediate
9087 
9088 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
9089 @*/
9090 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
9091 {
9092   DM             rdm;
9093   PetscFE        fe;
9094   PetscScalar   *r;
9095   PetscMPIInt    rank;
9096   DMPolytopeType ct;
9097   PetscInt       dim, cStart, cEnd, c;
9098   PetscBool      simplex;
9099 
9100   PetscFunctionBeginUser;
9101   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9102   PetscAssertPointer(ranks, 2);
9103   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
9104   PetscCall(DMClone(dm, &rdm));
9105   PetscCall(DMGetDimension(rdm, &dim));
9106   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
9107   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
9108   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
9109   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
9110   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
9111   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9112   PetscCall(PetscFEDestroy(&fe));
9113   PetscCall(DMCreateDS(rdm));
9114   PetscCall(DMCreateGlobalVector(rdm, ranks));
9115   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
9116   PetscCall(VecGetArray(*ranks, &r));
9117   for (c = cStart; c < cEnd; ++c) {
9118     PetscScalar *lr;
9119 
9120     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
9121     if (lr) *lr = rank;
9122   }
9123   PetscCall(VecRestoreArray(*ranks, &r));
9124   PetscCall(DMDestroy(&rdm));
9125   PetscFunctionReturn(PETSC_SUCCESS);
9126 }
9127 
9128 /*@
9129   DMPlexCreateLabelField - Create a field whose value is the label value for that point
9130 
9131   Input Parameters:
9132 + dm    - The `DMPLEX`
9133 - label - The `DMLabel`
9134 
9135   Output Parameter:
9136 . val - The label value field
9137 
9138   Options Database Key:
9139 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
9140 
9141   Level: intermediate
9142 
9143 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
9144 @*/
9145 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
9146 {
9147   DM             rdm, plex;
9148   Vec            lval;
9149   PetscSection   section;
9150   PetscFE        fe;
9151   PetscScalar   *v;
9152   PetscInt       dim, pStart, pEnd, p, cStart;
9153   DMPolytopeType ct;
9154   char           name[PETSC_MAX_PATH_LEN];
9155   const char    *lname, *prefix;
9156 
9157   PetscFunctionBeginUser;
9158   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9159   PetscAssertPointer(label, 2);
9160   PetscAssertPointer(val, 3);
9161   PetscCall(DMClone(dm, &rdm));
9162   PetscCall(DMConvert(rdm, DMPLEX, &plex));
9163   PetscCall(DMPlexGetHeightStratum(plex, 0, &cStart, NULL));
9164   PetscCall(DMPlexGetCellType(plex, cStart, &ct));
9165   PetscCall(DMDestroy(&plex));
9166   PetscCall(DMGetDimension(rdm, &dim));
9167   PetscCall(DMGetOptionsPrefix(dm, &prefix));
9168   PetscCall(PetscObjectGetName((PetscObject)label, &lname));
9169   PetscCall(PetscSNPrintf(name, sizeof(name), "%s%s_", prefix ? prefix : "", lname));
9170   PetscCall(PetscFECreateByCell(PETSC_COMM_SELF, dim, 1, ct, name, -1, &fe));
9171   PetscCall(PetscObjectSetName((PetscObject)fe, ""));
9172   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9173   PetscCall(PetscFEDestroy(&fe));
9174   PetscCall(DMCreateDS(rdm));
9175   PetscCall(DMCreateGlobalVector(rdm, val));
9176   PetscCall(DMCreateLocalVector(rdm, &lval));
9177   PetscCall(PetscObjectSetName((PetscObject)*val, lname));
9178   PetscCall(DMGetLocalSection(rdm, &section));
9179   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
9180   PetscCall(VecGetArray(lval, &v));
9181   for (p = pStart; p < pEnd; ++p) {
9182     PetscInt cval, dof, off;
9183 
9184     PetscCall(PetscSectionGetDof(section, p, &dof));
9185     if (!dof) continue;
9186     PetscCall(DMLabelGetValue(label, p, &cval));
9187     PetscCall(PetscSectionGetOffset(section, p, &off));
9188     for (PetscInt d = 0; d < dof; d++) v[off + d] = cval;
9189   }
9190   PetscCall(VecRestoreArray(lval, &v));
9191   PetscCall(DMLocalToGlobal(rdm, lval, INSERT_VALUES, *val));
9192   PetscCall(VecDestroy(&lval));
9193   PetscCall(DMDestroy(&rdm));
9194   PetscFunctionReturn(PETSC_SUCCESS);
9195 }
9196 
9197 /*@
9198   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
9199 
9200   Input Parameter:
9201 . dm - The `DMPLEX` object
9202 
9203   Level: developer
9204 
9205   Notes:
9206   This is a useful diagnostic when creating meshes programmatically.
9207 
9208   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9209 
9210 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9211 @*/
9212 PetscErrorCode DMPlexCheckSymmetry(DM dm)
9213 {
9214   PetscSection    coneSection, supportSection;
9215   const PetscInt *cone, *support;
9216   PetscInt        coneSize, c, supportSize, s;
9217   PetscInt        pStart, pEnd, p, pp, csize, ssize;
9218   PetscBool       storagecheck = PETSC_TRUE;
9219 
9220   PetscFunctionBegin;
9221   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9222   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
9223   PetscCall(DMPlexGetConeSection(dm, &coneSection));
9224   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
9225   /* Check that point p is found in the support of its cone points, and vice versa */
9226   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9227   for (p = pStart; p < pEnd; ++p) {
9228     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
9229     PetscCall(DMPlexGetCone(dm, p, &cone));
9230     for (c = 0; c < coneSize; ++c) {
9231       PetscBool dup = PETSC_FALSE;
9232       PetscInt  d;
9233       for (d = c - 1; d >= 0; --d) {
9234         if (cone[c] == cone[d]) {
9235           dup = PETSC_TRUE;
9236           break;
9237         }
9238       }
9239       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
9240       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
9241       for (s = 0; s < supportSize; ++s) {
9242         if (support[s] == p) break;
9243       }
9244       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
9245         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
9246         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
9247         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9248         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
9249         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
9250         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9251         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]);
9252         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
9253       }
9254     }
9255     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
9256     if (p != pp) {
9257       storagecheck = PETSC_FALSE;
9258       continue;
9259     }
9260     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
9261     PetscCall(DMPlexGetSupport(dm, p, &support));
9262     for (s = 0; s < supportSize; ++s) {
9263       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
9264       PetscCall(DMPlexGetCone(dm, support[s], &cone));
9265       for (c = 0; c < coneSize; ++c) {
9266         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
9267         if (cone[c] != pp) {
9268           c = 0;
9269           break;
9270         }
9271         if (cone[c] == p) break;
9272       }
9273       if (c >= coneSize) {
9274         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
9275         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
9276         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9277         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
9278         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
9279         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9280         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
9281       }
9282     }
9283   }
9284   if (storagecheck) {
9285     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
9286     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
9287     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
9288   }
9289   PetscFunctionReturn(PETSC_SUCCESS);
9290 }
9291 
9292 /*
9293   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.
9294 */
9295 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
9296 {
9297   DMPolytopeType  cct;
9298   PetscInt        ptpoints[4];
9299   const PetscInt *cone, *ccone, *ptcone;
9300   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
9301 
9302   PetscFunctionBegin;
9303   *unsplit = 0;
9304   switch (ct) {
9305   case DM_POLYTOPE_POINT_PRISM_TENSOR:
9306     ptpoints[npt++] = c;
9307     break;
9308   case DM_POLYTOPE_SEG_PRISM_TENSOR:
9309     PetscCall(DMPlexGetCone(dm, c, &cone));
9310     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9311     for (cp = 0; cp < coneSize; ++cp) {
9312       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
9313       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
9314     }
9315     break;
9316   case DM_POLYTOPE_TRI_PRISM_TENSOR:
9317   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9318     PetscCall(DMPlexGetCone(dm, c, &cone));
9319     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9320     for (cp = 0; cp < coneSize; ++cp) {
9321       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
9322       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
9323       for (ccp = 0; ccp < cconeSize; ++ccp) {
9324         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
9325         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
9326           PetscInt p;
9327           for (p = 0; p < npt; ++p)
9328             if (ptpoints[p] == ccone[ccp]) break;
9329           if (p == npt) ptpoints[npt++] = ccone[ccp];
9330         }
9331       }
9332     }
9333     break;
9334   default:
9335     break;
9336   }
9337   for (pt = 0; pt < npt; ++pt) {
9338     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
9339     if (ptcone[0] == ptcone[1]) ++(*unsplit);
9340   }
9341   PetscFunctionReturn(PETSC_SUCCESS);
9342 }
9343 
9344 /*@
9345   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
9346 
9347   Input Parameters:
9348 + dm         - The `DMPLEX` object
9349 - cellHeight - Normally 0
9350 
9351   Level: developer
9352 
9353   Notes:
9354   This is a useful diagnostic when creating meshes programmatically.
9355   Currently applicable only to homogeneous simplex or tensor meshes.
9356 
9357   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9358 
9359 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9360 @*/
9361 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
9362 {
9363   DMPlexInterpolatedFlag interp;
9364   DMPolytopeType         ct;
9365   PetscInt               vStart, vEnd, cStart, cEnd, c;
9366 
9367   PetscFunctionBegin;
9368   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9369   PetscCall(DMPlexIsInterpolated(dm, &interp));
9370   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9371   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9372   for (c = cStart; c < cEnd; ++c) {
9373     PetscInt *closure = NULL;
9374     PetscInt  coneSize, closureSize, cl, Nv = 0;
9375 
9376     PetscCall(DMPlexGetCellType(dm, c, &ct));
9377     if (ct == DM_POLYTOPE_UNKNOWN) continue;
9378     if (interp == DMPLEX_INTERPOLATED_FULL) {
9379       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9380       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));
9381     }
9382     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9383     for (cl = 0; cl < closureSize * 2; cl += 2) {
9384       const PetscInt p = closure[cl];
9385       if ((p >= vStart) && (p < vEnd)) ++Nv;
9386     }
9387     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9388     /* Special Case: Tensor faces with identified vertices */
9389     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
9390       PetscInt unsplit;
9391 
9392       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9393       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
9394     }
9395     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));
9396   }
9397   PetscFunctionReturn(PETSC_SUCCESS);
9398 }
9399 
9400 /*@
9401   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
9402 
9403   Collective
9404 
9405   Input Parameters:
9406 + dm         - The `DMPLEX` object
9407 - cellHeight - Normally 0
9408 
9409   Level: developer
9410 
9411   Notes:
9412   This is a useful diagnostic when creating meshes programmatically.
9413   This routine is only relevant for meshes that are fully interpolated across all ranks.
9414   It will error out if a partially interpolated mesh is given on some rank.
9415   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
9416 
9417   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9418 
9419 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
9420 @*/
9421 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
9422 {
9423   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
9424   DMPlexInterpolatedFlag interpEnum;
9425 
9426   PetscFunctionBegin;
9427   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9428   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
9429   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
9430   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
9431     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
9432     PetscFunctionReturn(PETSC_SUCCESS);
9433   }
9434 
9435   PetscCall(DMGetDimension(dm, &dim));
9436   PetscCall(DMPlexGetDepth(dm, &depth));
9437   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9438   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
9439     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
9440     for (c = cStart; c < cEnd; ++c) {
9441       const PetscInt       *cone, *ornt, *faceSizes, *faces;
9442       const DMPolytopeType *faceTypes;
9443       DMPolytopeType        ct;
9444       PetscInt              numFaces, coneSize, f;
9445       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
9446 
9447       PetscCall(DMPlexGetCellType(dm, c, &ct));
9448       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9449       if (unsplit) continue;
9450       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9451       PetscCall(DMPlexGetCone(dm, c, &cone));
9452       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
9453       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9454       for (cl = 0; cl < closureSize * 2; cl += 2) {
9455         const PetscInt p = closure[cl];
9456         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
9457       }
9458       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9459       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);
9460       for (f = 0; f < numFaces; ++f) {
9461         DMPolytopeType fct;
9462         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
9463 
9464         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
9465         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
9466         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
9467           const PetscInt p = fclosure[cl];
9468           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
9469         }
9470         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]);
9471         for (v = 0; v < fnumCorners; ++v) {
9472           if (fclosure[v] != faces[fOff + v]) {
9473             PetscInt v1;
9474 
9475             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
9476             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
9477             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
9478             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
9479             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9480             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]);
9481           }
9482         }
9483         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
9484         fOff += faceSizes[f];
9485       }
9486       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9487       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9488     }
9489   }
9490   PetscFunctionReturn(PETSC_SUCCESS);
9491 }
9492 
9493 /*@
9494   DMPlexCheckGeometry - Check the geometry of mesh cells
9495 
9496   Input Parameter:
9497 . dm - The `DMPLEX` object
9498 
9499   Level: developer
9500 
9501   Notes:
9502   This is a useful diagnostic when creating meshes programmatically.
9503 
9504   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9505 
9506 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9507 @*/
9508 PetscErrorCode DMPlexCheckGeometry(DM dm)
9509 {
9510   Vec       coordinates;
9511   PetscReal detJ, J[9], refVol = 1.0;
9512   PetscReal vol;
9513   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9514 
9515   PetscFunctionBegin;
9516   PetscCall(DMGetDimension(dm, &dim));
9517   PetscCall(DMGetCoordinateDim(dm, &dE));
9518   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9519   PetscCall(DMPlexGetDepth(dm, &depth));
9520   for (d = 0; d < dim; ++d) refVol *= 2.0;
9521   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9522   /* Make sure local coordinates are created, because that step is collective */
9523   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9524   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9525   for (c = cStart; c < cEnd; ++c) {
9526     DMPolytopeType ct;
9527     PetscInt       unsplit;
9528     PetscBool      ignoreZeroVol = PETSC_FALSE;
9529 
9530     PetscCall(DMPlexGetCellType(dm, c, &ct));
9531     switch (ct) {
9532     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9533     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9534     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9535       ignoreZeroVol = PETSC_TRUE;
9536       break;
9537     default:
9538       break;
9539     }
9540     switch (ct) {
9541     case DM_POLYTOPE_TRI_PRISM:
9542     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9543     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9544     case DM_POLYTOPE_PYRAMID:
9545       continue;
9546     default:
9547       break;
9548     }
9549     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9550     if (unsplit) continue;
9551     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9552     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);
9553     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9554     /* This should work with periodicity since DG coordinates should be used */
9555     if (depth > 1) {
9556       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9557       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);
9558       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9559     }
9560   }
9561   PetscFunctionReturn(PETSC_SUCCESS);
9562 }
9563 
9564 /*@
9565   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9566 
9567   Collective
9568 
9569   Input Parameters:
9570 + dm              - The `DMPLEX` object
9571 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9572 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9573 
9574   Level: developer
9575 
9576   Notes:
9577   This is mainly intended for debugging/testing purposes.
9578 
9579   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9580 
9581   Extra roots can come from periodic cuts, where additional points appear on the boundary
9582 
9583 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9584 @*/
9585 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9586 {
9587   PetscInt           l, nleaves, nroots, overlap;
9588   const PetscInt    *locals;
9589   const PetscSFNode *remotes;
9590   PetscBool          distributed;
9591   MPI_Comm           comm;
9592   PetscMPIInt        rank;
9593 
9594   PetscFunctionBegin;
9595   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9596   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9597   else pointSF = dm->sf;
9598   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9599   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9600   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9601   {
9602     PetscMPIInt mpiFlag;
9603 
9604     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9605     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9606   }
9607   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9608   PetscCall(DMPlexIsDistributed(dm, &distributed));
9609   if (!distributed) {
9610     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);
9611     PetscFunctionReturn(PETSC_SUCCESS);
9612   }
9613   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);
9614   PetscCall(DMPlexGetOverlap(dm, &overlap));
9615 
9616   /* Check SF graph is compatible with DMPlex chart */
9617   {
9618     PetscInt pStart, pEnd, maxLeaf;
9619 
9620     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9621     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9622     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9623     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9624   }
9625 
9626   /* Check Point SF has no local points referenced */
9627   for (l = 0; l < nleaves; l++) {
9628     PetscMPIInt irank;
9629 
9630     PetscCall(PetscMPIIntCast(remotes[l].rank, &irank));
9631     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);
9632   }
9633 
9634   /* Check there are no cells in interface */
9635   if (!overlap) {
9636     PetscInt cellHeight, cStart, cEnd;
9637 
9638     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9639     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9640     for (l = 0; l < nleaves; ++l) {
9641       const PetscInt point = locals ? locals[l] : l;
9642 
9643       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9644     }
9645   }
9646 
9647   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9648   {
9649     const PetscInt *rootdegree;
9650 
9651     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9652     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9653     for (l = 0; l < nleaves; ++l) {
9654       const PetscInt  point = locals ? locals[l] : l;
9655       const PetscInt *cone;
9656       PetscInt        coneSize, c, idx;
9657 
9658       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9659       PetscCall(DMPlexGetCone(dm, point, &cone));
9660       for (c = 0; c < coneSize; ++c) {
9661         if (!rootdegree[cone[c]]) {
9662           if (locals) {
9663             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9664           } else {
9665             idx = (cone[c] < nleaves) ? cone[c] : -1;
9666           }
9667           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9668         }
9669       }
9670     }
9671   }
9672   PetscFunctionReturn(PETSC_SUCCESS);
9673 }
9674 
9675 /*@
9676   DMPlexCheckOrphanVertices - Check that no vertices are disconnected from the mesh, unless the mesh only consists of disconnected vertices.
9677 
9678   Collective
9679 
9680   Input Parameter:
9681 . dm - The `DMPLEX` object
9682 
9683   Level: developer
9684 
9685   Notes:
9686   This is mainly intended for debugging/testing purposes.
9687 
9688   Other cell types which are disconnected would be caught by the symmetry and face checks.
9689 
9690   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9691 
9692 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheck()`, `DMSetFromOptions()`
9693 @*/
9694 PetscErrorCode DMPlexCheckOrphanVertices(DM dm)
9695 {
9696   PetscInt pStart, pEnd, vStart, vEnd;
9697 
9698   PetscFunctionBegin;
9699   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9700   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9701   if (pStart == vStart && pEnd == vEnd) PetscFunctionReturn(PETSC_SUCCESS);
9702   for (PetscInt v = vStart; v < vEnd; ++v) {
9703     PetscInt suppSize;
9704 
9705     PetscCall(DMPlexGetSupportSize(dm, v, &suppSize));
9706     PetscCheck(suppSize, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Vertex %" PetscInt_FMT " is disconnected from the mesh", v);
9707   }
9708   PetscFunctionReturn(PETSC_SUCCESS);
9709 }
9710 
9711 /*@
9712   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9713 
9714   Input Parameter:
9715 . dm - The `DMPLEX` object
9716 
9717   Level: developer
9718 
9719   Notes:
9720   This is a useful diagnostic when creating meshes programmatically.
9721 
9722   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9723 
9724   Currently does not include `DMPlexCheckCellShape()`.
9725 
9726 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9727 @*/
9728 PetscErrorCode DMPlexCheck(DM dm)
9729 {
9730   PetscInt cellHeight;
9731 
9732   PetscFunctionBegin;
9733   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9734   PetscCall(DMPlexCheckSymmetry(dm));
9735   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9736   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9737   PetscCall(DMPlexCheckGeometry(dm));
9738   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9739   PetscCall(DMPlexCheckInterfaceCones(dm));
9740   PetscCall(DMPlexCheckOrphanVertices(dm));
9741   PetscFunctionReturn(PETSC_SUCCESS);
9742 }
9743 
9744 typedef struct cell_stats {
9745   PetscReal min, max, sum, squaresum;
9746   PetscInt  count;
9747 } cell_stats_t;
9748 
9749 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9750 {
9751   PetscInt i, N = *len;
9752 
9753   for (i = 0; i < N; i++) {
9754     cell_stats_t *A = (cell_stats_t *)a;
9755     cell_stats_t *B = (cell_stats_t *)b;
9756 
9757     B->min = PetscMin(A->min, B->min);
9758     B->max = PetscMax(A->max, B->max);
9759     B->sum += A->sum;
9760     B->squaresum += A->squaresum;
9761     B->count += A->count;
9762   }
9763 }
9764 
9765 /*@
9766   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9767 
9768   Collective
9769 
9770   Input Parameters:
9771 + dm        - The `DMPLEX` object
9772 . output    - If true, statistics will be displayed on `stdout`
9773 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9774 
9775   Level: developer
9776 
9777   Notes:
9778   This is mainly intended for debugging/testing purposes.
9779 
9780   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9781 
9782 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9783 @*/
9784 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9785 {
9786   DM           dmCoarse;
9787   cell_stats_t stats, globalStats;
9788   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9789   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9790   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9791   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9792   PetscMPIInt  rank, size;
9793 
9794   PetscFunctionBegin;
9795   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9796   stats.min = PETSC_MAX_REAL;
9797   stats.max = PETSC_MIN_REAL;
9798   stats.sum = stats.squaresum = 0.;
9799   stats.count                 = 0;
9800 
9801   PetscCallMPI(MPI_Comm_size(comm, &size));
9802   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9803   PetscCall(DMGetCoordinateDim(dm, &cdim));
9804   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9805   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9806   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9807   for (c = cStart; c < cEnd; c++) {
9808     PetscInt  i;
9809     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9810 
9811     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9812     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9813     for (i = 0; i < PetscSqr(cdim); ++i) {
9814       frobJ += J[i] * J[i];
9815       frobInvJ += invJ[i] * invJ[i];
9816     }
9817     cond2 = frobJ * frobInvJ;
9818     cond  = PetscSqrtReal(cond2);
9819 
9820     stats.min = PetscMin(stats.min, cond);
9821     stats.max = PetscMax(stats.max, cond);
9822     stats.sum += cond;
9823     stats.squaresum += cond2;
9824     stats.count++;
9825     if (output && cond > limit) {
9826       PetscSection coordSection;
9827       Vec          coordsLocal;
9828       PetscScalar *coords = NULL;
9829       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9830 
9831       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9832       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9833       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9834       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9835       for (i = 0; i < Nv / cdim; ++i) {
9836         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9837         for (d = 0; d < cdim; ++d) {
9838           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9839           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9840         }
9841         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9842       }
9843       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9844       for (cl = 0; cl < clSize * 2; cl += 2) {
9845         const PetscInt edge = closure[cl];
9846 
9847         if ((edge >= eStart) && (edge < eEnd)) {
9848           PetscReal len;
9849 
9850           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9851           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9852         }
9853       }
9854       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9855       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9856     }
9857   }
9858   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9859 
9860   if (size > 1) {
9861     PetscMPIInt  blockLengths[2] = {4, 1};
9862     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9863     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9864     MPI_Op       statReduce;
9865 
9866     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9867     PetscCallMPI(MPI_Type_commit(&statType));
9868     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9869     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9870     PetscCallMPI(MPI_Op_free(&statReduce));
9871     PetscCallMPI(MPI_Type_free(&statType));
9872   } else {
9873     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9874   }
9875   if (rank == 0) {
9876     count = globalStats.count;
9877     min   = globalStats.min;
9878     max   = globalStats.max;
9879     mean  = globalStats.sum / globalStats.count;
9880     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9881   }
9882 
9883   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));
9884   PetscCall(PetscFree2(J, invJ));
9885 
9886   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9887   if (dmCoarse) {
9888     PetscBool isplex;
9889 
9890     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9891     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9892   }
9893   PetscFunctionReturn(PETSC_SUCCESS);
9894 }
9895 
9896 /*@
9897   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9898   orthogonal quality below given tolerance.
9899 
9900   Collective
9901 
9902   Input Parameters:
9903 + dm   - The `DMPLEX` object
9904 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9905 - atol - [0, 1] Absolute tolerance for tagging cells.
9906 
9907   Output Parameters:
9908 + OrthQual      - `Vec` containing orthogonal quality per cell
9909 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9910 
9911   Options Database Keys:
9912 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9913 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9914 
9915   Level: intermediate
9916 
9917   Notes:
9918   Orthogonal quality is given by the following formula\:
9919 
9920   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9921 
9922   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
9923   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9924   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9925   calculating the cosine of the angle between these vectors.
9926 
9927   Orthogonal quality ranges from 1 (best) to 0 (worst).
9928 
9929   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9930   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9931 
9932   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9933 
9934 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9935 @*/
9936 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9937 {
9938   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9939   PetscInt              *idx;
9940   PetscScalar           *oqVals;
9941   const PetscScalar     *cellGeomArr, *faceGeomArr;
9942   PetscReal             *ci, *fi, *Ai;
9943   MPI_Comm               comm;
9944   Vec                    cellgeom, facegeom;
9945   DM                     dmFace, dmCell;
9946   IS                     glob;
9947   ISLocalToGlobalMapping ltog;
9948   PetscViewer            vwr;
9949 
9950   PetscFunctionBegin;
9951   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9952   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9953   PetscAssertPointer(OrthQual, 4);
9954   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9955   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9956   PetscCall(DMGetDimension(dm, &nc));
9957   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9958   {
9959     DMPlexInterpolatedFlag interpFlag;
9960 
9961     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9962     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9963       PetscMPIInt rank;
9964 
9965       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9966       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9967     }
9968   }
9969   if (OrthQualLabel) {
9970     PetscAssertPointer(OrthQualLabel, 5);
9971     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9972     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9973   } else {
9974     *OrthQualLabel = NULL;
9975   }
9976   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9977   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9978   PetscCall(DMPlexCreateCellNumbering(dm, PETSC_TRUE, &glob));
9979   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9980   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9981   PetscCall(VecCreate(comm, OrthQual));
9982   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9983   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9984   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9985   PetscCall(VecSetUp(*OrthQual));
9986   PetscCall(ISDestroy(&glob));
9987   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9988   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9989   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9990   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9991   PetscCall(VecGetDM(cellgeom, &dmCell));
9992   PetscCall(VecGetDM(facegeom, &dmFace));
9993   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9994   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9995     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9996     PetscInt         cellarr[2], *adj = NULL;
9997     PetscScalar     *cArr, *fArr;
9998     PetscReal        minvalc = 1.0, minvalf = 1.0;
9999     PetscFVCellGeom *cg;
10000 
10001     idx[cellIter] = cell - cStart;
10002     cellarr[0]    = cell;
10003     /* Make indexing into cellGeom easier */
10004     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
10005     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
10006     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
10007     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
10008     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
10009       PetscInt         i;
10010       const PetscInt   neigh  = adj[cellneigh];
10011       PetscReal        normci = 0, normfi = 0, normai = 0;
10012       PetscFVCellGeom *cgneigh;
10013       PetscFVFaceGeom *fg;
10014 
10015       /* Don't count ourselves in the neighbor list */
10016       if (neigh == cell) continue;
10017       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
10018       cellarr[1] = neigh;
10019       {
10020         PetscInt        numcovpts;
10021         const PetscInt *covpts;
10022 
10023         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
10024         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
10025         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
10026       }
10027 
10028       /* Compute c_i, f_i and their norms */
10029       for (i = 0; i < nc; i++) {
10030         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
10031         fi[i] = fg->centroid[i] - cg->centroid[i];
10032         Ai[i] = fg->normal[i];
10033         normci += PetscPowReal(ci[i], 2);
10034         normfi += PetscPowReal(fi[i], 2);
10035         normai += PetscPowReal(Ai[i], 2);
10036       }
10037       normci = PetscSqrtReal(normci);
10038       normfi = PetscSqrtReal(normfi);
10039       normai = PetscSqrtReal(normai);
10040 
10041       /* Normalize and compute for each face-cell-normal pair */
10042       for (i = 0; i < nc; i++) {
10043         ci[i] = ci[i] / normci;
10044         fi[i] = fi[i] / normfi;
10045         Ai[i] = Ai[i] / normai;
10046         /* PetscAbs because I don't know if normals are guaranteed to point out */
10047         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
10048         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
10049       }
10050       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
10051       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
10052     }
10053     PetscCall(PetscFree(adj));
10054     PetscCall(PetscFree2(cArr, fArr));
10055     /* Defer to cell if they're equal */
10056     oqVals[cellIter] = PetscMin(minvalf, minvalc);
10057     if (OrthQualLabel) {
10058       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
10059     }
10060   }
10061   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
10062   PetscCall(VecAssemblyBegin(*OrthQual));
10063   PetscCall(VecAssemblyEnd(*OrthQual));
10064   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
10065   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
10066   PetscCall(PetscOptionsCreateViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
10067   if (OrthQualLabel) {
10068     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
10069   }
10070   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
10071   PetscCall(PetscViewerDestroy(&vwr));
10072   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
10073   PetscFunctionReturn(PETSC_SUCCESS);
10074 }
10075 
10076 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
10077  * interpolator construction */
10078 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
10079 {
10080   PetscSection section, newSection, gsection;
10081   PetscSF      sf;
10082   PetscBool    hasConstraints, ghasConstraints;
10083 
10084   PetscFunctionBegin;
10085   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10086   PetscAssertPointer(odm, 2);
10087   PetscCall(DMGetLocalSection(dm, &section));
10088   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
10089   PetscCallMPI(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
10090   if (!ghasConstraints) {
10091     PetscCall(PetscObjectReference((PetscObject)dm));
10092     *odm = dm;
10093     PetscFunctionReturn(PETSC_SUCCESS);
10094   }
10095   PetscCall(DMClone(dm, odm));
10096   PetscCall(DMCopyFields(dm, PETSC_DETERMINE, PETSC_DETERMINE, *odm));
10097   PetscCall(DMGetLocalSection(*odm, &newSection));
10098   PetscCall(DMGetPointSF(*odm, &sf));
10099   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_TRUE, PETSC_FALSE, &gsection));
10100   PetscCall(DMSetGlobalSection(*odm, gsection));
10101   PetscCall(PetscSectionDestroy(&gsection));
10102   PetscFunctionReturn(PETSC_SUCCESS);
10103 }
10104 
10105 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
10106 {
10107   DM        dmco, dmfo;
10108   Mat       interpo;
10109   Vec       rscale;
10110   Vec       cglobalo, clocal;
10111   Vec       fglobal, fglobalo, flocal;
10112   PetscBool regular;
10113 
10114   PetscFunctionBegin;
10115   PetscCall(DMGetFullDM(dmc, &dmco));
10116   PetscCall(DMGetFullDM(dmf, &dmfo));
10117   PetscCall(DMSetCoarseDM(dmfo, dmco));
10118   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
10119   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
10120   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
10121   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
10122   PetscCall(DMCreateLocalVector(dmc, &clocal));
10123   PetscCall(VecSet(cglobalo, 0.));
10124   PetscCall(VecSet(clocal, 0.));
10125   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
10126   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
10127   PetscCall(DMCreateLocalVector(dmf, &flocal));
10128   PetscCall(VecSet(fglobal, 0.));
10129   PetscCall(VecSet(fglobalo, 0.));
10130   PetscCall(VecSet(flocal, 0.));
10131   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
10132   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
10133   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
10134   PetscCall(MatMult(interpo, cglobalo, fglobalo));
10135   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
10136   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
10137   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
10138   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
10139   *shift = fglobal;
10140   PetscCall(VecDestroy(&flocal));
10141   PetscCall(VecDestroy(&fglobalo));
10142   PetscCall(VecDestroy(&clocal));
10143   PetscCall(VecDestroy(&cglobalo));
10144   PetscCall(VecDestroy(&rscale));
10145   PetscCall(MatDestroy(&interpo));
10146   PetscCall(DMDestroy(&dmfo));
10147   PetscCall(DMDestroy(&dmco));
10148   PetscFunctionReturn(PETSC_SUCCESS);
10149 }
10150 
10151 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
10152 {
10153   PetscObject shifto;
10154   Vec         shift;
10155 
10156   PetscFunctionBegin;
10157   if (!interp) {
10158     Vec rscale;
10159 
10160     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
10161     PetscCall(VecDestroy(&rscale));
10162   } else {
10163     PetscCall(PetscObjectReference((PetscObject)interp));
10164   }
10165   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
10166   if (!shifto) {
10167     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
10168     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
10169     shifto = (PetscObject)shift;
10170     PetscCall(VecDestroy(&shift));
10171   }
10172   shift = (Vec)shifto;
10173   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
10174   PetscCall(VecAXPY(fineSol, 1.0, shift));
10175   PetscCall(MatDestroy(&interp));
10176   PetscFunctionReturn(PETSC_SUCCESS);
10177 }
10178 
10179 /* Pointwise interpolation
10180      Just code FEM for now
10181      u^f = I u^c
10182      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
10183      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
10184      I_{ij} = psi^f_i phi^c_j
10185 */
10186 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
10187 {
10188   PetscSection gsc, gsf;
10189   PetscInt     m, n;
10190   void        *ctx;
10191   DM           cdm;
10192   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
10193 
10194   PetscFunctionBegin;
10195   PetscCall(DMGetGlobalSection(dmFine, &gsf));
10196   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10197   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10198   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10199 
10200   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
10201   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
10202   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10203   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
10204   PetscCall(DMGetApplicationContext(dmFine, &ctx));
10205 
10206   PetscCall(DMGetCoarseDM(dmFine, &cdm));
10207   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10208   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
10209   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
10210   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
10211   if (scaling) {
10212     /* Use naive scaling */
10213     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
10214   }
10215   PetscFunctionReturn(PETSC_SUCCESS);
10216 }
10217 
10218 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
10219 {
10220   VecScatter ctx;
10221 
10222   PetscFunctionBegin;
10223   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
10224   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
10225   PetscCall(VecScatterDestroy(&ctx));
10226   PetscFunctionReturn(PETSC_SUCCESS);
10227 }
10228 
10229 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[])
10230 {
10231   const PetscInt f  = (PetscInt)PetscRealPart(constants[numConstants]);
10232   const PetscInt Nc = uOff[f + 1] - uOff[f];
10233   for (PetscInt c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
10234 }
10235 
10236 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *lmass, Vec *mass)
10237 {
10238   DM           dmc;
10239   PetscDS      ds;
10240   Vec          ones, locmass;
10241   IS           cellIS;
10242   PetscFormKey key;
10243   PetscInt     depth;
10244 
10245   PetscFunctionBegin;
10246   PetscCall(DMClone(dm, &dmc));
10247   PetscCall(DMCopyDisc(dm, dmc));
10248   PetscCall(DMGetDS(dmc, &ds));
10249   for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10250   if (mass) PetscCall(DMCreateGlobalVector(dm, mass));
10251   if (lmass) PetscCall(DMCreateLocalVector(dm, &locmass));
10252   else PetscCall(DMGetLocalVector(dm, &locmass));
10253   PetscCall(DMGetLocalVector(dm, &ones));
10254   PetscCall(DMPlexGetDepth(dm, &depth));
10255   PetscCall(DMGetStratumIS(dm, "depth", depth, &cellIS));
10256   PetscCall(VecSet(locmass, 0.0));
10257   PetscCall(VecSet(ones, 1.0));
10258   key.label = NULL;
10259   key.value = 0;
10260   key.field = 0;
10261   key.part  = 0;
10262   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
10263   PetscCall(ISDestroy(&cellIS));
10264   if (mass) {
10265     PetscCall(DMLocalToGlobalBegin(dm, locmass, ADD_VALUES, *mass));
10266     PetscCall(DMLocalToGlobalEnd(dm, locmass, ADD_VALUES, *mass));
10267   }
10268   PetscCall(DMRestoreLocalVector(dm, &ones));
10269   if (lmass) *lmass = locmass;
10270   else PetscCall(DMRestoreLocalVector(dm, &locmass));
10271   PetscCall(DMDestroy(&dmc));
10272   PetscFunctionReturn(PETSC_SUCCESS);
10273 }
10274 
10275 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
10276 {
10277   PetscSection gsc, gsf;
10278   PetscInt     m, n;
10279   void        *ctx;
10280   DM           cdm;
10281   PetscBool    regular;
10282 
10283   PetscFunctionBegin;
10284   if (dmFine == dmCoarse) {
10285     DM            dmc;
10286     PetscDS       ds;
10287     PetscWeakForm wf;
10288     Vec           u;
10289     IS            cellIS;
10290     PetscFormKey  key;
10291     PetscInt      depth;
10292 
10293     PetscCall(DMClone(dmFine, &dmc));
10294     PetscCall(DMCopyDisc(dmFine, dmc));
10295     PetscCall(DMGetDS(dmc, &ds));
10296     PetscCall(PetscDSGetWeakForm(ds, &wf));
10297     PetscCall(PetscWeakFormClear(wf));
10298     for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10299     PetscCall(DMCreateMatrix(dmc, mass));
10300     PetscCall(DMGetLocalVector(dmc, &u));
10301     PetscCall(DMPlexGetDepth(dmc, &depth));
10302     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
10303     PetscCall(MatZeroEntries(*mass));
10304     key.label = NULL;
10305     key.value = 0;
10306     key.field = 0;
10307     key.part  = 0;
10308     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
10309     PetscCall(ISDestroy(&cellIS));
10310     PetscCall(DMRestoreLocalVector(dmc, &u));
10311     PetscCall(DMDestroy(&dmc));
10312   } else {
10313     PetscCall(DMGetGlobalSection(dmFine, &gsf));
10314     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10315     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10316     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10317 
10318     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
10319     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10320     PetscCall(MatSetType(*mass, dmCoarse->mattype));
10321     PetscCall(DMGetApplicationContext(dmFine, &ctx));
10322 
10323     PetscCall(DMGetCoarseDM(dmFine, &cdm));
10324     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10325     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
10326     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
10327   }
10328   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
10329   PetscFunctionReturn(PETSC_SUCCESS);
10330 }
10331 
10332 /*@
10333   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10334 
10335   Input Parameter:
10336 . dm - The `DMPLEX` object
10337 
10338   Output Parameter:
10339 . regular - The flag
10340 
10341   Level: intermediate
10342 
10343 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
10344 @*/
10345 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
10346 {
10347   PetscFunctionBegin;
10348   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10349   PetscAssertPointer(regular, 2);
10350   *regular = ((DM_Plex *)dm->data)->regularRefinement;
10351   PetscFunctionReturn(PETSC_SUCCESS);
10352 }
10353 
10354 /*@
10355   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10356 
10357   Input Parameters:
10358 + dm      - The `DMPLEX` object
10359 - regular - The flag
10360 
10361   Level: intermediate
10362 
10363 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
10364 @*/
10365 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
10366 {
10367   PetscFunctionBegin;
10368   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10369   ((DM_Plex *)dm->data)->regularRefinement = regular;
10370   PetscFunctionReturn(PETSC_SUCCESS);
10371 }
10372 
10373 /*@
10374   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
10375   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
10376 
10377   Not Collective
10378 
10379   Input Parameter:
10380 . dm - The `DMPLEX` object
10381 
10382   Output Parameters:
10383 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
10384 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
10385 
10386   Level: intermediate
10387 
10388 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
10389 @*/
10390 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
10391 {
10392   DM_Plex *plex = (DM_Plex *)dm->data;
10393 
10394   PetscFunctionBegin;
10395   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10396   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
10397   if (anchorSection) *anchorSection = plex->anchorSection;
10398   if (anchorIS) *anchorIS = plex->anchorIS;
10399   PetscFunctionReturn(PETSC_SUCCESS);
10400 }
10401 
10402 /*@
10403   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
10404 
10405   Collective
10406 
10407   Input Parameters:
10408 + dm            - The `DMPLEX` object
10409 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
10410                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10411 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10412 
10413   Level: intermediate
10414 
10415   Notes:
10416   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
10417   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
10418   combination of other points' degrees of freedom.
10419 
10420   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
10421   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
10422 
10423   The reference counts of `anchorSection` and `anchorIS` are incremented.
10424 
10425 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
10426 @*/
10427 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
10428 {
10429   DM_Plex    *plex = (DM_Plex *)dm->data;
10430   PetscMPIInt result;
10431 
10432   PetscFunctionBegin;
10433   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10434   if (anchorSection) {
10435     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
10436     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
10437     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
10438   }
10439   if (anchorIS) {
10440     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
10441     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
10442     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
10443   }
10444 
10445   PetscCall(PetscObjectReference((PetscObject)anchorSection));
10446   PetscCall(PetscSectionDestroy(&plex->anchorSection));
10447   plex->anchorSection = anchorSection;
10448 
10449   PetscCall(PetscObjectReference((PetscObject)anchorIS));
10450   PetscCall(ISDestroy(&plex->anchorIS));
10451   plex->anchorIS = anchorIS;
10452 
10453   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
10454     PetscInt        size, a, pStart, pEnd;
10455     const PetscInt *anchors;
10456 
10457     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10458     PetscCall(ISGetLocalSize(anchorIS, &size));
10459     PetscCall(ISGetIndices(anchorIS, &anchors));
10460     for (a = 0; a < size; a++) {
10461       PetscInt p;
10462 
10463       p = anchors[a];
10464       if (p >= pStart && p < pEnd) {
10465         PetscInt dof;
10466 
10467         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10468         if (dof) {
10469           PetscCall(ISRestoreIndices(anchorIS, &anchors));
10470           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
10471         }
10472       }
10473     }
10474     PetscCall(ISRestoreIndices(anchorIS, &anchors));
10475   }
10476   /* reset the generic constraints */
10477   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
10478   PetscFunctionReturn(PETSC_SUCCESS);
10479 }
10480 
10481 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
10482 {
10483   PetscSection anchorSection;
10484   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
10485 
10486   PetscFunctionBegin;
10487   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10488   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10489   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
10490   PetscCall(PetscSectionGetNumFields(section, &numFields));
10491   if (numFields) {
10492     PetscInt f;
10493     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
10494 
10495     for (f = 0; f < numFields; f++) {
10496       PetscInt numComp;
10497 
10498       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
10499       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
10500     }
10501   }
10502   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10503   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10504   pStart = PetscMax(pStart, sStart);
10505   pEnd   = PetscMin(pEnd, sEnd);
10506   pEnd   = PetscMax(pStart, pEnd);
10507   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
10508   for (p = pStart; p < pEnd; p++) {
10509     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10510     if (dof) {
10511       PetscCall(PetscSectionGetDof(section, p, &dof));
10512       PetscCall(PetscSectionSetDof(*cSec, p, dof));
10513       for (f = 0; f < numFields; f++) {
10514         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
10515         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
10516       }
10517     }
10518   }
10519   PetscCall(PetscSectionSetUp(*cSec));
10520   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
10521   PetscFunctionReturn(PETSC_SUCCESS);
10522 }
10523 
10524 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
10525 {
10526   PetscSection    aSec;
10527   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
10528   const PetscInt *anchors;
10529   PetscInt        numFields, f;
10530   IS              aIS;
10531   MatType         mtype;
10532   PetscBool       iscuda, iskokkos;
10533 
10534   PetscFunctionBegin;
10535   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10536   PetscCall(PetscSectionGetStorageSize(cSec, &m));
10537   PetscCall(PetscSectionGetStorageSize(section, &n));
10538   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
10539   PetscCall(MatSetSizes(*cMat, m, n, m, n));
10540   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
10541   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
10542   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
10543   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
10544   if (iscuda) mtype = MATSEQAIJCUSPARSE;
10545   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
10546   else mtype = MATSEQAIJ;
10547   PetscCall(MatSetType(*cMat, mtype));
10548   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
10549   PetscCall(ISGetIndices(aIS, &anchors));
10550   /* cSec will be a subset of aSec and section */
10551   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
10552   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10553   PetscCall(PetscMalloc1(m + 1, &i));
10554   i[0] = 0;
10555   PetscCall(PetscSectionGetNumFields(section, &numFields));
10556   for (p = pStart; p < pEnd; p++) {
10557     PetscInt rDof, rOff, r;
10558 
10559     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10560     if (!rDof) continue;
10561     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10562     if (numFields) {
10563       for (f = 0; f < numFields; f++) {
10564         annz = 0;
10565         for (r = 0; r < rDof; r++) {
10566           a = anchors[rOff + r];
10567           if (a < sStart || a >= sEnd) continue;
10568           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10569           annz += aDof;
10570         }
10571         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10572         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10573         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10574       }
10575     } else {
10576       annz = 0;
10577       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10578       for (q = 0; q < dof; q++) {
10579         a = anchors[rOff + q];
10580         if (a < sStart || a >= sEnd) continue;
10581         PetscCall(PetscSectionGetDof(section, a, &aDof));
10582         annz += aDof;
10583       }
10584       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10585       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10586       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10587     }
10588   }
10589   nnz = i[m];
10590   PetscCall(PetscMalloc1(nnz, &j));
10591   offset = 0;
10592   for (p = pStart; p < pEnd; p++) {
10593     if (numFields) {
10594       for (f = 0; f < numFields; f++) {
10595         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10596         for (q = 0; q < dof; q++) {
10597           PetscInt rDof, rOff, r;
10598           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10599           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10600           for (r = 0; r < rDof; r++) {
10601             PetscInt s;
10602 
10603             a = anchors[rOff + r];
10604             if (a < sStart || a >= sEnd) continue;
10605             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10606             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10607             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10608           }
10609         }
10610       }
10611     } else {
10612       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10613       for (q = 0; q < dof; q++) {
10614         PetscInt rDof, rOff, r;
10615         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10616         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10617         for (r = 0; r < rDof; r++) {
10618           PetscInt s;
10619 
10620           a = anchors[rOff + r];
10621           if (a < sStart || a >= sEnd) continue;
10622           PetscCall(PetscSectionGetDof(section, a, &aDof));
10623           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10624           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10625         }
10626       }
10627     }
10628   }
10629   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10630   PetscCall(PetscFree(i));
10631   PetscCall(PetscFree(j));
10632   PetscCall(ISRestoreIndices(aIS, &anchors));
10633   PetscFunctionReturn(PETSC_SUCCESS);
10634 }
10635 
10636 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10637 {
10638   DM_Plex     *plex = (DM_Plex *)dm->data;
10639   PetscSection anchorSection, section, cSec;
10640   Mat          cMat;
10641 
10642   PetscFunctionBegin;
10643   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10644   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10645   if (anchorSection) {
10646     PetscInt Nf;
10647 
10648     PetscCall(DMGetLocalSection(dm, &section));
10649     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10650     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10651     PetscCall(DMGetNumFields(dm, &Nf));
10652     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10653     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10654     PetscCall(PetscSectionDestroy(&cSec));
10655     PetscCall(MatDestroy(&cMat));
10656   }
10657   PetscFunctionReturn(PETSC_SUCCESS);
10658 }
10659 
10660 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10661 {
10662   IS           subis;
10663   PetscSection section, subsection;
10664 
10665   PetscFunctionBegin;
10666   PetscCall(DMGetLocalSection(dm, &section));
10667   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10668   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10669   /* Create subdomain */
10670   PetscCall(DMPlexFilter(dm, label, value, PETSC_FALSE, PETSC_FALSE, NULL, subdm));
10671   /* Create submodel */
10672   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10673   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10674   PetscCall(DMSetLocalSection(*subdm, subsection));
10675   PetscCall(PetscSectionDestroy(&subsection));
10676   PetscCall(DMCopyDisc(dm, *subdm));
10677   /* Create map from submodel to global model */
10678   if (is) {
10679     PetscSection    sectionGlobal, subsectionGlobal;
10680     IS              spIS;
10681     const PetscInt *spmap;
10682     PetscInt       *subIndices;
10683     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10684     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10685 
10686     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10687     PetscCall(ISGetIndices(spIS, &spmap));
10688     PetscCall(PetscSectionGetNumFields(section, &Nf));
10689     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10690     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10691     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10692     for (p = pStart; p < pEnd; ++p) {
10693       PetscInt gdof, pSubSize = 0;
10694 
10695       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10696       if (gdof > 0) {
10697         for (f = 0; f < Nf; ++f) {
10698           PetscInt fdof, fcdof;
10699 
10700           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10701           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10702           pSubSize += fdof - fcdof;
10703         }
10704         subSize += pSubSize;
10705         if (pSubSize) {
10706           if (bs < 0) {
10707             bs = pSubSize;
10708           } else if (bs != pSubSize) {
10709             /* Layout does not admit a pointwise block size */
10710             bs = 1;
10711           }
10712         }
10713       }
10714     }
10715     /* Must have same blocksize on all procs (some might have no points) */
10716     bsLocal[0] = bs < 0 ? PETSC_INT_MAX : bs;
10717     bsLocal[1] = bs;
10718     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10719     if (bsMinMax[0] != bsMinMax[1]) {
10720       bs = 1;
10721     } else {
10722       bs = bsMinMax[0];
10723     }
10724     PetscCall(PetscMalloc1(subSize, &subIndices));
10725     for (p = pStart; p < pEnd; ++p) {
10726       PetscInt gdof, goff;
10727 
10728       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10729       if (gdof > 0) {
10730         const PetscInt point = spmap[p];
10731 
10732         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10733         for (f = 0; f < Nf; ++f) {
10734           PetscInt fdof, fcdof, fc, f2, poff = 0;
10735 
10736           /* Can get rid of this loop by storing field information in the global section */
10737           for (f2 = 0; f2 < f; ++f2) {
10738             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10739             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10740             poff += fdof - fcdof;
10741           }
10742           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10743           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10744           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10745         }
10746       }
10747     }
10748     PetscCall(ISRestoreIndices(spIS, &spmap));
10749     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10750     if (bs > 1) {
10751       /* We need to check that the block size does not come from non-contiguous fields */
10752       PetscInt i, j, set = 1;
10753       for (i = 0; i < subSize; i += bs) {
10754         for (j = 0; j < bs; ++j) {
10755           if (subIndices[i + j] != subIndices[i] + j) {
10756             set = 0;
10757             break;
10758           }
10759         }
10760       }
10761       if (set) PetscCall(ISSetBlockSize(*is, bs));
10762     }
10763     /* Attach nullspace */
10764     for (f = 0; f < Nf; ++f) {
10765       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10766       if ((*subdm)->nullspaceConstructors[f]) break;
10767     }
10768     if (f < Nf) {
10769       MatNullSpace nullSpace;
10770       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10771 
10772       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10773       PetscCall(MatNullSpaceDestroy(&nullSpace));
10774     }
10775   }
10776   PetscFunctionReturn(PETSC_SUCCESS);
10777 }
10778 
10779 /*@
10780   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10781 
10782   Input Parameters:
10783 + dm    - The `DM`
10784 - dummy - unused argument
10785 
10786   Options Database Key:
10787 . -dm_plex_monitor_throughput - Activate the monitor
10788 
10789   Level: developer
10790 
10791 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10792 @*/
10793 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10794 {
10795   PetscLogHandler default_handler;
10796 
10797   PetscFunctionBegin;
10798   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10799   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10800   if (default_handler) {
10801     PetscLogEvent      event;
10802     PetscEventPerfInfo eventInfo;
10803     PetscLogDouble     cellRate, flopRate;
10804     PetscInt           cStart, cEnd, Nf, N;
10805     const char        *name;
10806 
10807     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10808     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10809     PetscCall(DMGetNumFields(dm, &Nf));
10810     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10811     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10812     N        = (cEnd - cStart) * Nf * eventInfo.count;
10813     flopRate = eventInfo.flops / eventInfo.time;
10814     cellRate = N / eventInfo.time;
10815     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));
10816   } else {
10817     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.");
10818   }
10819   PetscFunctionReturn(PETSC_SUCCESS);
10820 }
10821