xref: /petsc/src/dm/impls/plex/plex.c (revision 6e3bbc6d02e241e461f9b8ad9a65cc097cb0596e)
1 #include <petsc/private/dmpleximpl.h> /*I      "petscdmplex.h"   I*/
2 #include <petsc/private/dmlabelimpl.h>
3 #include <petsc/private/isimpl.h>
4 #include <petsc/private/vecimpl.h>
5 #include <petsc/private/glvisvecimpl.h>
6 #include <petscsf.h>
7 #include <petscds.h>
8 #include <petscdraw.h>
9 #include <petscdmfield.h>
10 #include <petscdmplextransform.h>
11 #include <petscblaslapack.h>
12 
13 /* Logging support */
14 PetscLogEvent DMPLEX_Interpolate, DMPLEX_Partition, DMPLEX_Distribute, DMPLEX_DistributeCones, DMPLEX_DistributeLabels, DMPLEX_DistributeSF, DMPLEX_DistributeOverlap, DMPLEX_DistributeField, DMPLEX_DistributeData, DMPLEX_Migrate, DMPLEX_InterpolateSF, DMPLEX_GlobalToNaturalBegin, DMPLEX_GlobalToNaturalEnd, DMPLEX_NaturalToGlobalBegin, DMPLEX_NaturalToGlobalEnd, DMPLEX_Stratify, DMPLEX_Symmetrize, DMPLEX_Preallocate, DMPLEX_ResidualFEM, DMPLEX_JacobianFEM, DMPLEX_InterpolatorFEM, DMPLEX_InjectorFEM, DMPLEX_IntegralFEM, DMPLEX_CreateGmsh, DMPLEX_CreateBoxSFC, DMPLEX_RebalanceSharedPoints, DMPLEX_PartSelf, DMPLEX_PartLabelInvert, DMPLEX_PartLabelCreateSF, DMPLEX_PartStratSF, DMPLEX_CreatePointSF, DMPLEX_LocatePoints, DMPLEX_TopologyView, DMPLEX_LabelsView, DMPLEX_CoordinatesView, DMPLEX_SectionView, DMPLEX_GlobalVectorView, DMPLEX_LocalVectorView, DMPLEX_TopologyLoad, DMPLEX_LabelsLoad, DMPLEX_CoordinatesLoad, DMPLEX_SectionLoad, DMPLEX_GlobalVectorLoad, DMPLEX_LocalVectorLoad;
15 PetscLogEvent DMPLEX_RebalBuildGraph, DMPLEX_RebalRewriteSF, DMPLEX_RebalGatherGraph, DMPLEX_RebalPartition, DMPLEX_RebalScatterPart, DMPLEX_Generate, DMPLEX_Transform, DMPLEX_GetLocalOffsets, DMPLEX_Uninterpolate;
16 
17 /* Logging support */
18 PetscLogEvent DMPLEX_DistributionView, DMPLEX_DistributionLoad;
19 
20 PetscBool  Plexcite       = PETSC_FALSE;
21 const char PlexCitation[] = "@article{LangeMitchellKnepleyGorman2015,\n"
22                             "title     = {Efficient mesh management in {Firedrake} using {PETSc-DMPlex}},\n"
23                             "author    = {Michael Lange and Lawrence Mitchell and Matthew G. Knepley and Gerard J. Gorman},\n"
24                             "journal   = {SIAM Journal on Scientific Computing},\n"
25                             "volume    = {38},\n"
26                             "number    = {5},\n"
27                             "pages     = {S143--S155},\n"
28                             "eprint    = {http://arxiv.org/abs/1506.07749},\n"
29                             "doi       = {10.1137/15M1026092},\n"
30                             "year      = {2016},\n"
31                             "petsc_uses={DMPlex},\n}\n";
32 
33 PETSC_SINGLE_LIBRARY_INTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
34 
35 /*@
36   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
37 
38   Input Parameter:
39 . dm - The `DMPLEX` object
40 
41   Output Parameter:
42 . simplex - Flag checking for a simplex
43 
44   Level: intermediate
45 
46   Note:
47   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
48   If the mesh has no cells, this returns `PETSC_FALSE`.
49 
50 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
51 @*/
52 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
53 {
54   DMPolytopeType ct;
55   PetscInt       cStart, cEnd;
56 
57   PetscFunctionBegin;
58   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
59   if (cEnd <= cStart) {
60     *simplex = PETSC_FALSE;
61     PetscFunctionReturn(PETSC_SUCCESS);
62   }
63   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
64   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
65   PetscFunctionReturn(PETSC_SUCCESS);
66 }
67 
68 /*@
69   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
70 
71   Input Parameters:
72 + dm     - The `DMPLEX` object
73 - height - The cell height in the Plex, 0 is the default
74 
75   Output Parameters:
76 + cStart - The first "normal" cell, pass `NULL` if not needed
77 - cEnd   - The upper bound on "normal" cells, pass `NULL` if not needed
78 
79   Level: developer
80 
81   Note:
82   This function requires that tensor cells are ordered last.
83 
84 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetCellTypeStratum()`
85 @*/
86 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PeOp PetscInt *cStart, PeOp PetscInt *cEnd)
87 {
88   DMLabel         ctLabel;
89   IS              valueIS;
90   const PetscInt *ctypes;
91   PetscBool       found = PETSC_FALSE;
92   PetscInt        Nct, cS = PETSC_INT_MAX, cE = 0;
93 
94   PetscFunctionBegin;
95   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
96   PetscCall(DMLabelGetValueIS(ctLabel, &valueIS));
97   PetscCall(ISGetLocalSize(valueIS, &Nct));
98   PetscCall(ISGetIndices(valueIS, &ctypes));
99   for (PetscInt t = 0; t < Nct; ++t) {
100     const DMPolytopeType ct = (DMPolytopeType)ctypes[t];
101     PetscInt             ctS, ctE, ht;
102 
103     if (ct == DM_POLYTOPE_UNKNOWN) {
104       // If any cells are not typed, just use all cells
105       PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), cStart, cEnd));
106       break;
107     }
108     if (DMPolytopeTypeIsHybrid(ct) || ct == DM_POLYTOPE_FV_GHOST) continue;
109     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &ctS, &ctE));
110     if (ctS >= ctE) continue;
111     // Check that a point has the right height
112     PetscCall(DMPlexGetPointHeight(dm, ctS, &ht));
113     if (ht != height) continue;
114     cS    = PetscMin(cS, ctS);
115     cE    = PetscMax(cE, ctE);
116     found = PETSC_TRUE;
117   }
118   if (!Nct || !found) cS = cE = 0;
119   PetscCall(ISDestroy(&valueIS));
120   // Reset label for fast lookup
121   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
122   if (cStart) *cStart = cS;
123   if (cEnd) *cEnd = cE;
124   PetscFunctionReturn(PETSC_SUCCESS);
125 }
126 
127 PetscErrorCode DMPlexGetFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **ssStart, PetscInt **ssEnd, PetscViewerVTKFieldType **sft)
128 {
129   PetscInt                 cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd, c, depth, cellHeight, t;
130   PetscInt                *sStart, *sEnd;
131   PetscViewerVTKFieldType *ft;
132   PetscInt                 vcdof[DM_NUM_POLYTOPES + 1], globalvcdof[DM_NUM_POLYTOPES + 1];
133   DMLabel                  depthLabel, ctLabel;
134 
135   PetscFunctionBegin;
136   /* the vcdof and globalvcdof are sized to allow every polytope type and simple vertex at DM_NUM_POLYTOPES */
137   PetscCall(PetscArrayzero(vcdof, DM_NUM_POLYTOPES + 1));
138   PetscCall(DMGetCoordinateDim(dm, &cdim));
139   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
140   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
141   if (field >= 0) {
142     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[DM_NUM_POLYTOPES]));
143   } else {
144     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[DM_NUM_POLYTOPES]));
145   }
146 
147   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
148   PetscCall(DMPlexGetDepth(dm, &depth));
149   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
150   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
151   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
152     const DMPolytopeType ict = (DMPolytopeType)c;
153     PetscInt             dep;
154 
155     if (ict == DM_POLYTOPE_FV_GHOST) continue;
156     PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
157     if (pStart >= 0) {
158       PetscCall(DMLabelGetValue(depthLabel, cStart, &dep));
159       if (dep != depth - cellHeight) continue;
160     }
161     if (field >= 0) {
162       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[c]));
163     } else {
164       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[c]));
165     }
166   }
167 
168   PetscCallMPI(MPIU_Allreduce(vcdof, globalvcdof, DM_NUM_POLYTOPES + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
169   *types = 0;
170 
171   for (c = 0; c < DM_NUM_POLYTOPES + 1; ++c) {
172     if (globalvcdof[c]) ++(*types);
173   }
174 
175   PetscCall(PetscMalloc3(*types, &sStart, *types, &sEnd, *types, &ft));
176   t = 0;
177   if (globalvcdof[DM_NUM_POLYTOPES]) {
178     sStart[t] = vStart;
179     sEnd[t]   = vEnd;
180     ft[t]     = (globalvcdof[t] == cdim) ? PETSC_VTK_POINT_VECTOR_FIELD : PETSC_VTK_POINT_FIELD;
181     ++t;
182   }
183 
184   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
185     if (globalvcdof[c]) {
186       const DMPolytopeType ict = (DMPolytopeType)c;
187 
188       PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
189       sStart[t] = cStart;
190       sEnd[t]   = cEnd;
191       ft[t]     = (globalvcdof[c] == cdim) ? PETSC_VTK_CELL_VECTOR_FIELD : PETSC_VTK_CELL_FIELD;
192       ++t;
193     }
194   }
195 
196   if (!*types) {
197     if (field >= 0) {
198       const char *fieldname;
199 
200       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
201       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
202     } else {
203       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
204     }
205   }
206 
207   *ssStart = sStart;
208   *ssEnd   = sEnd;
209   *sft     = ft;
210   PetscFunctionReturn(PETSC_SUCCESS);
211 }
212 
213 PetscErrorCode DMPlexRestoreFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **sStart, PetscInt **sEnd, PetscViewerVTKFieldType **ft)
214 {
215   PetscFunctionBegin;
216   PetscCall(PetscFree3(*sStart, *sEnd, *ft));
217   PetscFunctionReturn(PETSC_SUCCESS);
218 }
219 
220 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
221 {
222   PetscInt cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
223   PetscInt vcdof[2] = {0, 0}, globalvcdof[2];
224 
225   PetscFunctionBegin;
226   *ft = PETSC_VTK_INVALID;
227   PetscCall(DMGetCoordinateDim(dm, &cdim));
228   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
229   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
230   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
231   if (field >= 0) {
232     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
233     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
234   } else {
235     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
236     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
237   }
238   PetscCallMPI(MPIU_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
239   if (globalvcdof[0]) {
240     *sStart = vStart;
241     *sEnd   = vEnd;
242     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
243     else *ft = PETSC_VTK_POINT_FIELD;
244   } else if (globalvcdof[1]) {
245     *sStart = cStart;
246     *sEnd   = cEnd;
247     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
248     else *ft = PETSC_VTK_CELL_FIELD;
249   } else {
250     if (field >= 0) {
251       const char *fieldname;
252 
253       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
254       PetscCall(PetscInfo(dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
255     } else {
256       PetscCall(PetscInfo(dm, "Could not classify VTK output type of section\n"));
257     }
258   }
259   PetscFunctionReturn(PETSC_SUCCESS);
260 }
261 
262 /*@
263   DMPlexVecView1D - Plot many 1D solutions on the same line graph
264 
265   Collective
266 
267   Input Parameters:
268 + dm     - The `DMPLEX` object
269 . n      - The number of vectors
270 . u      - The array of local vectors
271 - viewer - The `PetscViewer`
272 
273   Level: advanced
274 
275 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `VecViewFromOptions()`, `VecView()`
276 @*/
277 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
278 {
279   DM                 cdm;
280   PetscDS            ds;
281   PetscDraw          draw = NULL;
282   PetscDrawLG        lg;
283   Vec                coordinates;
284   const PetscScalar *coords, **sol;
285   PetscReal         *vals;
286   PetscInt          *Nc;
287   PetscInt           Nf, Nl, vStart, vEnd, eStart, eEnd;
288   char             **names;
289 
290   PetscFunctionBegin;
291   PetscCall(DMGetCoordinateDM(dm, &cdm));
292   PetscCall(DMGetDS(dm, &ds));
293   PetscCall(PetscDSGetNumFields(ds, &Nf));
294   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
295   PetscCall(PetscDSGetComponents(ds, &Nc));
296 
297   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
298   if (!draw) PetscFunctionReturn(PETSC_SUCCESS);
299   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
300 
301   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
302   for (PetscInt i = 0, l = 0; i < n; ++i) {
303     const char *vname;
304 
305     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
306     for (PetscInt f = 0; f < Nf; ++f) {
307       PetscObject disc;
308       const char *fname;
309       char        tmpname[PETSC_MAX_PATH_LEN];
310 
311       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
312       /* TODO Create names for components */
313       for (PetscInt c = 0; c < Nc[f]; ++c, ++l) {
314         PetscCall(PetscObjectGetName(disc, &fname));
315         PetscCall(PetscStrncpy(tmpname, vname, sizeof(tmpname)));
316         PetscCall(PetscStrlcat(tmpname, ":", sizeof(tmpname)));
317         PetscCall(PetscStrlcat(tmpname, fname, sizeof(tmpname)));
318         PetscCall(PetscStrallocpy(tmpname, &names[l]));
319       }
320     }
321   }
322   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
323   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
324   PetscCall(VecGetArrayRead(coordinates, &coords));
325   for (PetscInt i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
326   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
327   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
328   PetscSection s;
329   PetscInt     cdof, vdof;
330 
331   PetscCall(DMGetLocalSection(dm, &s));
332   PetscCall(PetscSectionGetDof(s, eStart, &cdof));
333   PetscCall(PetscSectionGetDof(s, vStart, &vdof));
334   if (cdof) {
335     if (vdof) {
336       // P_2
337       PetscInt vFirst = -1;
338 
339       for (PetscInt e = eStart; e < eEnd; ++e) {
340         PetscScalar    *xa, *xb, *svals;
341         const PetscInt *cone;
342 
343         PetscCall(DMPlexGetCone(dm, e, &cone));
344         PetscCall(DMPlexPointLocalRead(cdm, cone[0], coords, &xa));
345         PetscCall(DMPlexPointLocalRead(cdm, cone[1], coords, &xb));
346         if (e == eStart) vFirst = cone[0];
347         for (PetscInt i = 0; i < n; ++i) {
348           PetscCall(DMPlexPointLocalRead(dm, cone[0], sol[i], &svals));
349           for (PetscInt l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
350         }
351         PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(xa[0]), vals));
352         if (e == eEnd - 1 && cone[1] != vFirst) {
353           for (PetscInt i = 0; i < n; ++i) {
354             PetscCall(DMPlexPointLocalRead(dm, e, sol[i], &svals));
355             for (PetscInt l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
356           }
357           PetscCall(PetscDrawLGAddCommonPoint(lg, 0.5 * (PetscRealPart(xa[0]) + PetscRealPart(xb[0])), vals));
358           for (PetscInt i = 0; i < n; ++i) {
359             PetscCall(DMPlexPointLocalRead(dm, cone[1], sol[i], &svals));
360             for (PetscInt l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
361           }
362           PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(xb[0]), vals));
363         }
364       }
365     } else {
366       // P_0
367       for (PetscInt e = eStart; e < eEnd; ++e) {
368         PetscScalar    *xa, *xb, *svals;
369         const PetscInt *cone;
370 
371         PetscCall(DMPlexGetCone(dm, e, &cone));
372         PetscCall(DMPlexPointLocalRead(cdm, cone[0], coords, &xa));
373         PetscCall(DMPlexPointLocalRead(cdm, cone[1], coords, &xb));
374         for (PetscInt i = 0; i < n; ++i) {
375           PetscCall(DMPlexPointLocalRead(dm, e, sol[i], &svals));
376           for (PetscInt l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
377         }
378         PetscCall(PetscDrawLGAddCommonPoint(lg, 0.5 * (PetscRealPart(xa[0]) + PetscRealPart(xb[0])), vals));
379       }
380     }
381   } else if (vdof) {
382     // P_1
383     for (PetscInt v = vStart; v < vEnd; ++v) {
384       PetscScalar *x, *svals;
385 
386       PetscCall(DMPlexPointLocalRead(cdm, v, coords, &x));
387       for (PetscInt i = 0; i < n; ++i) {
388         PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
389         for (PetscInt l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
390       }
391       PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
392     }
393   } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Discretization not supported");
394   PetscCall(VecRestoreArrayRead(coordinates, &coords));
395   for (PetscInt i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
396   for (PetscInt l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
397   PetscCall(PetscFree3(sol, names, vals));
398 
399   PetscCall(PetscDrawLGDraw(lg));
400   PetscCall(PetscDrawLGDestroy(&lg));
401   PetscFunctionReturn(PETSC_SUCCESS);
402 }
403 
404 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
405 {
406   DM dm;
407 
408   PetscFunctionBegin;
409   PetscCall(VecGetDM(u, &dm));
410   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
411   PetscFunctionReturn(PETSC_SUCCESS);
412 }
413 
414 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
415 {
416   DM                 dm;
417   PetscSection       s;
418   PetscDraw          draw, popup;
419   DM                 cdm;
420   PetscSection       coordSection;
421   Vec                coordinates;
422   const PetscScalar *array;
423   PetscReal          lbound[3], ubound[3];
424   PetscReal          vbound[2], time;
425   PetscBool          flg;
426   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
427   const char        *name;
428   char               title[PETSC_MAX_PATH_LEN];
429 
430   PetscFunctionBegin;
431   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
432   PetscCall(VecGetDM(v, &dm));
433   PetscCall(DMGetCoordinateDim(dm, &dim));
434   PetscCall(DMGetLocalSection(dm, &s));
435   PetscCall(PetscSectionGetNumFields(s, &Nf));
436   PetscCall(DMGetCoarsenLevel(dm, &level));
437   PetscCall(DMGetCoordinateDM(dm, &cdm));
438   PetscCall(DMGetLocalSection(cdm, &coordSection));
439   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
440   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
441   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
442 
443   PetscCall(PetscObjectGetName((PetscObject)v, &name));
444   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
445 
446   PetscCall(VecGetLocalSize(coordinates, &N));
447   PetscCall(DMGetBoundingBox(dm, lbound, ubound));
448   PetscCall(PetscDrawClear(draw));
449 
450   /* Could implement something like DMDASelectFields() */
451   for (f = 0; f < Nf; ++f) {
452     DM          fdm = dm;
453     Vec         fv  = v;
454     IS          fis;
455     char        prefix[PETSC_MAX_PATH_LEN];
456     const char *fname;
457 
458     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
459     PetscCall(PetscSectionGetFieldName(s, f, &fname));
460 
461     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
462     else prefix[0] = '\0';
463     if (Nf > 1) {
464       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
465       PetscCall(VecGetSubVector(v, fis, &fv));
466       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
467       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
468     }
469     for (comp = 0; comp < Nc; ++comp, ++w) {
470       PetscInt nmax = 2;
471 
472       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
473       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
474       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
475       PetscCall(PetscDrawSetTitle(draw, title));
476 
477       /* TODO Get max and min only for this component */
478       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
479       if (!flg) {
480         PetscCall(VecMin(fv, NULL, &vbound[0]));
481         PetscCall(VecMax(fv, NULL, &vbound[1]));
482         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
483       }
484 
485       PetscCall(PetscDrawGetPopup(draw, &popup));
486       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
487       PetscCall(PetscDrawSetCoordinates(draw, lbound[0], lbound[1], ubound[0], ubound[1]));
488       PetscCall(VecGetArrayRead(fv, &array));
489       for (c = cStart; c < cEnd; ++c) {
490         DMPolytopeType     ct;
491         PetscScalar       *coords = NULL, *a = NULL;
492         const PetscScalar *coords_arr;
493         PetscBool          isDG;
494         PetscInt           numCoords;
495         int                color[4] = {-1, -1, -1, -1};
496 
497         PetscCall(DMPlexGetCellType(dm, c, &ct));
498         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
499         if (a) {
500           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
501           color[1] = color[2] = color[3] = color[0];
502         } else {
503           PetscScalar *vals = NULL;
504           PetscInt     numVals, va;
505 
506           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
507           if (!numVals) {
508             PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
509             continue;
510           }
511           PetscCheck(numVals % Nc == 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "The number of components %" PetscInt_FMT " does not divide the number of values in the closure %" PetscInt_FMT, Nc, numVals);
512           switch (numVals / Nc) {
513           case 1: /* P1 Clamped Segment Prism */
514           case 2: /* P1 Segment Prism, P2 Clamped Segment Prism */
515             PetscCheck(ct == DM_POLYTOPE_SEG_PRISM_TENSOR, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell should be a tensor segment, but it is a %s", DMPolytopeTypes[ct]);
516             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
517             break;
518           case 3: /* P1 Triangle */
519           case 4: /* P1 Quadrangle */
520             PetscCheck(ct == DM_POLYTOPE_TRIANGLE || ct == DM_POLYTOPE_QUADRILATERAL || ct == DM_POLYTOPE_SEG_PRISM_TENSOR, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell should be a triangle or quad, but it is a %s", DMPolytopeTypes[ct]);
521             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
522             break;
523           case 6: /* P2 Triangle */
524           case 8: /* P2 Quadrangle */
525             PetscCheck(ct == DM_POLYTOPE_TRIANGLE || ct == DM_POLYTOPE_QUADRILATERAL || ct == DM_POLYTOPE_SEG_PRISM_TENSOR, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell should be a triangle or quad, but it is a %s", DMPolytopeTypes[ct]);
526             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
527             break;
528           default:
529             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
530           }
531           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
532         }
533         PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
534         switch (numCoords) {
535         case 6:
536         case 12: /* Localized triangle */
537           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
538           break;
539         case 8:
540         case 16: /* Localized quadrilateral */
541           if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR) {
542             PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscMax(color[0], color[1])));
543           } else {
544             PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
545             PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), color[2], color[3], color[0]));
546           }
547           break;
548         default:
549           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
550         }
551         PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
552       }
553       PetscCall(VecRestoreArrayRead(fv, &array));
554       PetscCall(PetscDrawFlush(draw));
555       PetscCall(PetscDrawPause(draw));
556       PetscCall(PetscDrawSave(draw));
557     }
558     if (Nf > 1) {
559       PetscCall(VecRestoreSubVector(v, fis, &fv));
560       PetscCall(ISDestroy(&fis));
561       PetscCall(DMDestroy(&fdm));
562     }
563   }
564   PetscFunctionReturn(PETSC_SUCCESS);
565 }
566 
567 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
568 {
569   DM        dm;
570   PetscDraw draw;
571   PetscInt  dim;
572   PetscBool isnull;
573 
574   PetscFunctionBegin;
575   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
576   PetscCall(PetscDrawIsNull(draw, &isnull));
577   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
578 
579   PetscCall(VecGetDM(v, &dm));
580   PetscCall(DMGetCoordinateDim(dm, &dim));
581   switch (dim) {
582   case 1:
583     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
584     break;
585   case 2:
586     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
587     break;
588   default:
589     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
590   }
591   PetscFunctionReturn(PETSC_SUCCESS);
592 }
593 
594 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
595 {
596   DM                      dm;
597   Vec                     locv;
598   const char             *name;
599   PetscSection            section;
600   PetscInt                pStart, pEnd;
601   PetscInt                numFields;
602   PetscViewerVTKFieldType ft;
603 
604   PetscFunctionBegin;
605   PetscCall(VecGetDM(v, &dm));
606   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
607   PetscCall(PetscObjectGetName((PetscObject)v, &name));
608   PetscCall(PetscObjectSetName((PetscObject)locv, name));
609   PetscCall(VecCopy(v, locv));
610   PetscCall(DMGetLocalSection(dm, &section));
611   PetscCall(PetscSectionGetNumFields(section, &numFields));
612   if (!numFields) {
613     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
614     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
615   } else {
616     PetscInt f;
617 
618     for (f = 0; f < numFields; f++) {
619       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
620       if (ft == PETSC_VTK_INVALID) continue;
621       PetscCall(PetscObjectReference((PetscObject)locv));
622       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
623     }
624     PetscCall(VecDestroy(&locv));
625   }
626   PetscFunctionReturn(PETSC_SUCCESS);
627 }
628 
629 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
630 {
631   DM        dm;
632   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns, ispython;
633 
634   PetscFunctionBegin;
635   PetscCall(VecGetDM(v, &dm));
636   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
637   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
638   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
639   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
640   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
641   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
642   PetscCall(PetscObjectHasFunction((PetscObject)viewer, "PetscViewerPythonViewObject_C", &ispython));
643   if (isvtk || ishdf5 || isdraw || isglvis || iscgns || ispython) {
644     PetscInt    i, numFields;
645     PetscObject fe;
646     PetscBool   fem  = PETSC_FALSE;
647     Vec         locv = v;
648     const char *name;
649     PetscInt    step;
650     PetscReal   time;
651 
652     PetscCall(DMGetNumFields(dm, &numFields));
653     for (i = 0; i < numFields; i++) {
654       PetscCall(DMGetField(dm, i, NULL, &fe));
655       if (fe->classid == PETSCFE_CLASSID) {
656         fem = PETSC_TRUE;
657         break;
658       }
659     }
660     if (fem) {
661       PetscObject isZero;
662 
663       PetscCall(DMGetLocalVector(dm, &locv));
664       PetscCall(PetscObjectGetName((PetscObject)v, &name));
665       PetscCall(PetscObjectSetName((PetscObject)locv, name));
666       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
667       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
668       PetscCall(VecCopy(v, locv));
669       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
670       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
671     }
672     if (isvtk) {
673       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
674     } else if (ishdf5) {
675 #if defined(PETSC_HAVE_HDF5)
676       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
677 #else
678       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
679 #endif
680     } else if (isdraw) {
681       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
682     } else if (ispython) {
683       PetscCall(PetscViewerPythonViewObject(viewer, (PetscObject)locv));
684     } else if (isglvis) {
685       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
686       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
687       PetscCall(VecView_GLVis(locv, viewer));
688     } else if (iscgns) {
689 #if defined(PETSC_HAVE_CGNS)
690       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
691 #else
692       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
693 #endif
694     }
695     if (fem) {
696       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
697       PetscCall(DMRestoreLocalVector(dm, &locv));
698     }
699   } else {
700     PetscBool isseq;
701 
702     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
703     if (isseq) PetscCall(VecView_Seq(v, viewer));
704     else PetscCall(VecView_MPI(v, viewer));
705   }
706   PetscFunctionReturn(PETSC_SUCCESS);
707 }
708 
709 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
710 {
711   DM        dm;
712   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns, ispython;
713 
714   PetscFunctionBegin;
715   PetscCall(VecGetDM(v, &dm));
716   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
717   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
718   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
719   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
720   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
721   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
722   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
723   PetscCall(PetscObjectHasFunction((PetscObject)viewer, "PetscViewerPythonViewObject_C", &ispython));
724   if (isvtk || isdraw || isglvis || iscgns || ispython) {
725     Vec         locv;
726     PetscObject isZero;
727     const char *name;
728 
729     PetscCall(DMGetLocalVector(dm, &locv));
730     PetscCall(PetscObjectGetName((PetscObject)v, &name));
731     PetscCall(PetscObjectSetName((PetscObject)locv, name));
732     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
733     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
734     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
735     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
736     PetscCall(VecView_Plex_Local(locv, viewer));
737     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
738     PetscCall(DMRestoreLocalVector(dm, &locv));
739   } else if (ishdf5) {
740 #if defined(PETSC_HAVE_HDF5)
741     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
742 #else
743     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
744 #endif
745   } else if (isexodusii) {
746 #if defined(PETSC_HAVE_EXODUSII)
747     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
748 #else
749     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
750 #endif
751   } else {
752     PetscBool isseq;
753 
754     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
755     if (isseq) PetscCall(VecView_Seq(v, viewer));
756     else PetscCall(VecView_MPI(v, viewer));
757   }
758   PetscFunctionReturn(PETSC_SUCCESS);
759 }
760 
761 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
762 {
763   DM                dm;
764   MPI_Comm          comm;
765   PetscViewerFormat format;
766   Vec               v;
767   PetscBool         isvtk, ishdf5;
768 
769   PetscFunctionBegin;
770   PetscCall(VecGetDM(originalv, &dm));
771   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
772   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
773   PetscCall(PetscViewerGetFormat(viewer, &format));
774   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
775   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
776   if (format == PETSC_VIEWER_NATIVE) {
777     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
778     /* this need a better fix */
779     if (dm->useNatural) {
780       if (dm->sfNatural) {
781         const char *vecname;
782         PetscInt    n, nroots;
783 
784         PetscCall(VecGetLocalSize(originalv, &n));
785         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
786         if (n == nroots) {
787           PetscCall(DMPlexCreateNaturalVector(dm, &v));
788           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
789           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
790           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
791           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
792         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
793       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
794     } else v = originalv;
795   } else v = originalv;
796 
797   if (ishdf5) {
798 #if defined(PETSC_HAVE_HDF5)
799     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
800 #else
801     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
802 #endif
803   } else if (isvtk) {
804     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
805   } else {
806     PetscBool isseq;
807 
808     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
809     if (isseq) PetscCall(VecView_Seq(v, viewer));
810     else PetscCall(VecView_MPI(v, viewer));
811   }
812   if (v != originalv) PetscCall(VecDestroy(&v));
813   PetscFunctionReturn(PETSC_SUCCESS);
814 }
815 
816 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
817 {
818   DM        dm;
819   PetscBool ishdf5;
820 
821   PetscFunctionBegin;
822   PetscCall(VecGetDM(v, &dm));
823   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
824   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
825   if (ishdf5) {
826     DM          dmBC;
827     Vec         gv;
828     const char *name;
829 
830     PetscCall(DMGetOutputDM(dm, &dmBC));
831     PetscCall(DMGetGlobalVector(dmBC, &gv));
832     PetscCall(PetscObjectGetName((PetscObject)v, &name));
833     PetscCall(PetscObjectSetName((PetscObject)gv, name));
834     PetscCall(VecLoad_Default(gv, viewer));
835     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
836     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
837     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
838   } else PetscCall(VecLoad_Default(v, viewer));
839   PetscFunctionReturn(PETSC_SUCCESS);
840 }
841 
842 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
843 {
844   DM        dm;
845   PetscBool ishdf5, isexodusii, iscgns;
846 
847   PetscFunctionBegin;
848   PetscCall(VecGetDM(v, &dm));
849   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
850   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
851   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
852   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
853   if (ishdf5) {
854 #if defined(PETSC_HAVE_HDF5)
855     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
856 #else
857     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
858 #endif
859   } else if (isexodusii) {
860 #if defined(PETSC_HAVE_EXODUSII)
861     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
862 #else
863     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
864 #endif
865   } else if (iscgns) {
866 #if defined(PETSC_HAVE_CGNS)
867     PetscCall(VecLoad_Plex_CGNS_Internal(v, viewer));
868 #else
869     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
870 #endif
871   } else PetscCall(VecLoad_Default(v, viewer));
872   PetscFunctionReturn(PETSC_SUCCESS);
873 }
874 
875 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
876 {
877   DM                dm;
878   PetscViewerFormat format;
879   PetscBool         ishdf5;
880 
881   PetscFunctionBegin;
882   PetscCall(VecGetDM(originalv, &dm));
883   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
884   PetscCall(PetscViewerGetFormat(viewer, &format));
885   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
886   if (format == PETSC_VIEWER_NATIVE) {
887     if (dm->useNatural) {
888       if (dm->sfNatural) {
889         if (ishdf5) {
890 #if defined(PETSC_HAVE_HDF5)
891           Vec         v;
892           const char *vecname;
893 
894           PetscCall(DMPlexCreateNaturalVector(dm, &v));
895           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
896           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
897           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
898           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
899           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
900           PetscCall(VecDestroy(&v));
901 #else
902           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
903 #endif
904         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
905       }
906     } else PetscCall(VecLoad_Default(originalv, viewer));
907   }
908   PetscFunctionReturn(PETSC_SUCCESS);
909 }
910 
911 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
912 {
913   PetscSection       coordSection;
914   Vec                coordinates;
915   DMLabel            depthLabel, celltypeLabel;
916   const char        *name[4];
917   const PetscScalar *a;
918   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
919 
920   PetscFunctionBegin;
921   PetscCall(DMGetDimension(dm, &dim));
922   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
923   PetscCall(DMGetCoordinateSection(dm, &coordSection));
924   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
925   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
926   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
927   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
928   PetscCall(VecGetArrayRead(coordinates, &a));
929   name[0]       = "vertex";
930   name[1]       = "edge";
931   name[dim - 1] = "face";
932   name[dim]     = "cell";
933   for (c = cStart; c < cEnd; ++c) {
934     PetscInt *closure = NULL;
935     PetscInt  closureSize, cl, ct;
936 
937     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
938     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
939     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
940     PetscCall(PetscViewerASCIIPushTab(viewer));
941     for (cl = 0; cl < closureSize * 2; cl += 2) {
942       PetscInt point = closure[cl], depth, dof, off, d, p;
943 
944       if ((point < pStart) || (point >= pEnd)) continue;
945       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
946       if (!dof) continue;
947       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
948       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
949       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
950       for (p = 0; p < dof / dim; ++p) {
951         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
952         for (d = 0; d < dim; ++d) {
953           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
954           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
955         }
956         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
957       }
958       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
959     }
960     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
961     PetscCall(PetscViewerASCIIPopTab(viewer));
962   }
963   PetscCall(VecRestoreArrayRead(coordinates, &a));
964   PetscFunctionReturn(PETSC_SUCCESS);
965 }
966 
967 typedef enum {
968   CS_CARTESIAN,
969   CS_POLAR,
970   CS_CYLINDRICAL,
971   CS_SPHERICAL
972 } CoordSystem;
973 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
974 
975 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
976 {
977   PetscInt i;
978 
979   PetscFunctionBegin;
980   if (dim > 3) {
981     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
982   } else {
983     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
984 
985     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
986     switch (cs) {
987     case CS_CARTESIAN:
988       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
989       break;
990     case CS_POLAR:
991       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
992       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
993       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
994       break;
995     case CS_CYLINDRICAL:
996       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
997       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
998       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
999       trcoords[2] = coords[2];
1000       break;
1001     case CS_SPHERICAL:
1002       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
1003       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
1004       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
1005       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
1006       break;
1007     }
1008     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
1009   }
1010   PetscFunctionReturn(PETSC_SUCCESS);
1011 }
1012 
1013 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
1014 {
1015   DM_Plex          *mesh = (DM_Plex *)dm->data;
1016   DM                cdm, cdmCell;
1017   PetscSection      coordSection, coordSectionCell;
1018   Vec               coordinates, coordinatesCell;
1019   PetscViewerFormat format;
1020 
1021   PetscFunctionBegin;
1022   PetscCall(PetscViewerGetFormat(viewer, &format));
1023   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
1024     const char *name;
1025     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
1026     PetscInt    pStart, pEnd, p, numLabels, l;
1027     PetscMPIInt rank, size;
1028 
1029     PetscCall(DMGetCoordinateDM(dm, &cdm));
1030     PetscCall(DMGetCoordinateSection(dm, &coordSection));
1031     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1032     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
1033     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
1034     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
1035     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1036     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1037     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1038     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1039     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
1040     PetscCall(DMGetDimension(dm, &dim));
1041     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1042     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1043     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1044     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1045     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
1046     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1047     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
1048     for (p = pStart; p < pEnd; ++p) {
1049       PetscInt dof, off, s;
1050 
1051       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
1052       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
1053       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
1054     }
1055     PetscCall(PetscViewerFlush(viewer));
1056     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
1057     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
1058     for (p = pStart; p < pEnd; ++p) {
1059       PetscInt dof, off, c;
1060 
1061       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
1062       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
1063       for (c = off; c < off + dof; ++c) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " <---- %" PetscInt_FMT " (%" PetscInt_FMT ")\n", rank, p, mesh->cones[c], mesh->coneOrientations[c]));
1064     }
1065     PetscCall(PetscViewerFlush(viewer));
1066     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1067     if (coordSection && coordinates) {
1068       CoordSystem        cs = CS_CARTESIAN;
1069       const PetscScalar *array, *arrayCell = NULL;
1070       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_INT_MAX, pcEnd = PETSC_INT_MIN, pStart, pEnd, p;
1071       PetscMPIInt        rank;
1072       const char        *name;
1073 
1074       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
1075       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
1076       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
1077       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
1078       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
1079       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
1080       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
1081       pStart = PetscMin(pvStart, pcStart);
1082       pEnd   = PetscMax(pvEnd, pcEnd);
1083       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
1084       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
1085       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
1086       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
1087 
1088       PetscCall(VecGetArrayRead(coordinates, &array));
1089       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
1090       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1091       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
1092       for (p = pStart; p < pEnd; ++p) {
1093         PetscInt dof, off;
1094 
1095         if (p >= pvStart && p < pvEnd) {
1096           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
1097           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
1098           if (dof) {
1099             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dof %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1100             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
1101             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1102           }
1103         }
1104         if (cdmCell && p >= pcStart && p < pcEnd) {
1105           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
1106           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
1107           if (dof) {
1108             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dof %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1109             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
1110             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1111           }
1112         }
1113       }
1114       PetscCall(PetscViewerFlush(viewer));
1115       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1116       PetscCall(VecRestoreArrayRead(coordinates, &array));
1117       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
1118     }
1119     PetscCall(DMGetNumLabels(dm, &numLabels));
1120     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1121     for (l = 0; l < numLabels; ++l) {
1122       DMLabel     label;
1123       PetscBool   isdepth;
1124       const char *name;
1125 
1126       PetscCall(DMGetLabelName(dm, l, &name));
1127       PetscCall(PetscStrcmp(name, "depth", &isdepth));
1128       if (isdepth) continue;
1129       PetscCall(DMGetLabel(dm, name, &label));
1130       PetscCall(DMLabelView(label, viewer));
1131     }
1132     if (size > 1) {
1133       PetscSF sf;
1134 
1135       PetscCall(DMGetPointSF(dm, &sf));
1136       PetscCall(PetscSFView(sf, viewer));
1137     }
1138     if (mesh->periodic.face_sfs)
1139       for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFView(mesh->periodic.face_sfs[i], viewer));
1140     PetscCall(PetscViewerFlush(viewer));
1141   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
1142     const char  *name, *color;
1143     const char  *defcolors[3]  = {"gray", "orange", "green"};
1144     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
1145     char         lname[PETSC_MAX_PATH_LEN];
1146     PetscReal    scale      = 2.0;
1147     PetscReal    tikzscale  = 1.0;
1148     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
1149     double       tcoords[3];
1150     PetscScalar *coords;
1151     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, fStart = 0, fEnd = 0, e, p, n;
1152     PetscMPIInt  rank, size;
1153     char       **names, **colors, **lcolors;
1154     PetscBool    flg, lflg;
1155     PetscBT      wp = NULL;
1156     PetscInt     pEnd, pStart;
1157 
1158     PetscCall(DMGetCoordinateDM(dm, &cdm));
1159     PetscCall(DMGetCoordinateSection(dm, &coordSection));
1160     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1161     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
1162     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
1163     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
1164     PetscCall(DMGetDimension(dm, &dim));
1165     PetscCall(DMPlexGetDepth(dm, &depth));
1166     PetscCall(DMGetNumLabels(dm, &numLabels));
1167     numLabels  = PetscMax(numLabels, 10);
1168     numColors  = 10;
1169     numLColors = 10;
1170     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
1171     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
1172     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
1173     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
1174     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
1175     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
1176     n = 4;
1177     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
1178     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1179     n = 4;
1180     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
1181     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1182     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
1183     if (!useLabels) numLabels = 0;
1184     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
1185     if (!useColors) {
1186       numColors = 3;
1187       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
1188     }
1189     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
1190     if (!useColors) {
1191       numLColors = 4;
1192       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
1193     }
1194     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
1195     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
1196     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
1197     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
1198     if (depth < dim) plotEdges = PETSC_FALSE;
1199     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
1200 
1201     /* filter points with labelvalue != labeldefaultvalue */
1202     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1203     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1204     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1205     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1206     PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
1207     if (lflg) {
1208       DMLabel lbl;
1209 
1210       PetscCall(DMGetLabel(dm, lname, &lbl));
1211       if (lbl) {
1212         PetscInt val, defval;
1213 
1214         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1215         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1216         for (c = pStart; c < pEnd; c++) {
1217           PetscInt *closure = NULL;
1218           PetscInt  closureSize;
1219 
1220           PetscCall(DMLabelGetValue(lbl, c, &val));
1221           if (val == defval) continue;
1222 
1223           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1224           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1225           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1226         }
1227       }
1228     }
1229 
1230     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1231     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1232     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1233     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1234 \\documentclass[tikz]{standalone}\n\n\
1235 \\usepackage{pgflibraryshapes}\n\
1236 \\usetikzlibrary{backgrounds}\n\
1237 \\usetikzlibrary{arrows}\n\
1238 \\begin{document}\n"));
1239     if (size > 1) {
1240       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1241       for (p = 0; p < size; ++p) {
1242         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1243         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1244       }
1245       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1246     }
1247     if (drawHasse) {
1248       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, PetscMax(fEnd - fStart, cEnd - cStart)));
1249 
1250       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1251       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1252       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1253       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1254       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1255       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1256       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1257       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1258       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fStart}{%" PetscInt_FMT "}\n", fStart));
1259       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fEnd}{%" PetscInt_FMT "}\n", fEnd - 1));
1260       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fShift}{%.2f}\n", 3 + (maxStratum - (fEnd - fStart)) / 2.));
1261       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numFaces}{%" PetscInt_FMT "}\n", fEnd - fStart));
1262       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1263       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1264       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1265       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1266     }
1267     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1268 
1269     /* Plot vertices */
1270     PetscCall(VecGetArray(coordinates, &coords));
1271     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1272     for (v = vStart; v < vEnd; ++v) {
1273       PetscInt  off, dof, d;
1274       PetscBool isLabeled = PETSC_FALSE;
1275 
1276       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1277       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1278       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1279       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1280       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1281       for (d = 0; d < dof; ++d) {
1282         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1283         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1284       }
1285       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1286       if (dim == 3) {
1287         PetscReal tmp = tcoords[1];
1288         tcoords[1]    = tcoords[2];
1289         tcoords[2]    = -tmp;
1290       }
1291       for (d = 0; d < dof; ++d) {
1292         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1293         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", tcoords[d]));
1294       }
1295       if (drawHasse) color = colors[0 % numColors];
1296       else color = colors[rank % numColors];
1297       for (l = 0; l < numLabels; ++l) {
1298         PetscInt val;
1299         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1300         if (val >= 0) {
1301           color     = lcolors[l % numLColors];
1302           isLabeled = PETSC_TRUE;
1303           break;
1304         }
1305       }
1306       if (drawNumbers[0]) {
1307         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1308       } else if (drawColors[0]) {
1309         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1310       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1311     }
1312     PetscCall(VecRestoreArray(coordinates, &coords));
1313     PetscCall(PetscViewerFlush(viewer));
1314     /* Plot edges */
1315     if (plotEdges) {
1316       PetscCall(VecGetArray(coordinates, &coords));
1317       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1318       for (e = eStart; e < eEnd; ++e) {
1319         const PetscInt *cone;
1320         PetscInt        coneSize, offA, offB, dof, d;
1321 
1322         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1323         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1324         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1325         PetscCall(DMPlexGetCone(dm, e, &cone));
1326         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1327         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1328         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1329         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1330         for (d = 0; d < dof; ++d) {
1331           tcoords[d] = (double)(scale * PetscRealPart(coords[offA + d] + coords[offB + d]) / 2);
1332           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1333         }
1334         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1335         if (dim == 3) {
1336           PetscReal tmp = tcoords[1];
1337           tcoords[1]    = tcoords[2];
1338           tcoords[2]    = -tmp;
1339         }
1340         for (d = 0; d < dof; ++d) {
1341           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1342           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", tcoords[d]));
1343         }
1344         if (drawHasse) color = colors[1 % numColors];
1345         else color = colors[rank % numColors];
1346         for (l = 0; l < numLabels; ++l) {
1347           PetscInt val;
1348           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1349           if (val >= 0) {
1350             color = lcolors[l % numLColors];
1351             break;
1352           }
1353         }
1354         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1355       }
1356       PetscCall(VecRestoreArray(coordinates, &coords));
1357       PetscCall(PetscViewerFlush(viewer));
1358       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1359     }
1360     /* Plot cells */
1361     if (dim == 3 || !drawNumbers[1]) {
1362       for (e = eStart; e < eEnd; ++e) {
1363         const PetscInt *cone;
1364 
1365         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1366         color = colors[rank % numColors];
1367         for (l = 0; l < numLabels; ++l) {
1368           PetscInt val;
1369           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1370           if (val >= 0) {
1371             color = lcolors[l % numLColors];
1372             break;
1373           }
1374         }
1375         PetscCall(DMPlexGetCone(dm, e, &cone));
1376         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1377       }
1378     } else {
1379       DMPolytopeType ct;
1380 
1381       /* Drawing a 2D polygon */
1382       for (c = cStart; c < cEnd; ++c) {
1383         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1384         PetscCall(DMPlexGetCellType(dm, c, &ct));
1385         if (DMPolytopeTypeIsHybrid(ct)) {
1386           const PetscInt *cone;
1387           PetscInt        coneSize, e;
1388 
1389           PetscCall(DMPlexGetCone(dm, c, &cone));
1390           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1391           for (e = 0; e < coneSize; ++e) {
1392             const PetscInt *econe;
1393 
1394             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1395             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", colors[rank % numColors], econe[0], rank, cone[e], rank, econe[1], rank));
1396           }
1397         } else {
1398           PetscInt *closure = NULL;
1399           PetscInt  closureSize, Nv = 0, v;
1400 
1401           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1402           for (p = 0; p < closureSize * 2; p += 2) {
1403             const PetscInt point = closure[p];
1404 
1405             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1406           }
1407           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1408           for (v = 0; v <= Nv; ++v) {
1409             const PetscInt vertex = closure[v % Nv];
1410 
1411             if (v > 0) {
1412               if (plotEdges) {
1413                 const PetscInt *edge;
1414                 PetscInt        endpoints[2], ne;
1415 
1416                 endpoints[0] = closure[v - 1];
1417                 endpoints[1] = vertex;
1418                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1419                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1420                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1421                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1422               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1423             }
1424             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1425           }
1426           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1427           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1428         }
1429       }
1430     }
1431     for (c = cStart; c < cEnd; ++c) {
1432       double             ccoords[3] = {0.0, 0.0, 0.0};
1433       PetscBool          isLabeled  = PETSC_FALSE;
1434       PetscScalar       *cellCoords = NULL;
1435       const PetscScalar *array;
1436       PetscInt           numCoords, cdim, d;
1437       PetscBool          isDG;
1438 
1439       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1440       PetscCall(DMGetCoordinateDim(dm, &cdim));
1441       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1442       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1443       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1444       for (p = 0; p < numCoords / cdim; ++p) {
1445         for (d = 0; d < cdim; ++d) {
1446           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1447           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1448         }
1449         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1450         if (cdim == 3) {
1451           PetscReal tmp = tcoords[1];
1452           tcoords[1]    = tcoords[2];
1453           tcoords[2]    = -tmp;
1454         }
1455         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1456       }
1457       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1458       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1459       for (d = 0; d < cdim; ++d) {
1460         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1461         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", ccoords[d]));
1462       }
1463       if (drawHasse) color = colors[depth % numColors];
1464       else color = colors[rank % numColors];
1465       for (l = 0; l < numLabels; ++l) {
1466         PetscInt val;
1467         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1468         if (val >= 0) {
1469           color     = lcolors[l % numLColors];
1470           isLabeled = PETSC_TRUE;
1471           break;
1472         }
1473       }
1474       if (drawNumbers[dim]) {
1475         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1476       } else if (drawColors[dim]) {
1477         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1478       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1479     }
1480     if (drawHasse) {
1481       int height = 0;
1482 
1483       color = colors[depth % numColors];
1484       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1485       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1486       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1487       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,%d) {\\c};\n", rank, color, height++));
1488       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1489 
1490       if (depth > 2) {
1491         color = colors[1 % numColors];
1492         PetscCall(PetscViewerASCIIPrintf(viewer, "%% Faces\n"));
1493         PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\f in {\\fStart,...,\\fEnd}\n"));
1494         PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1495         PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\f_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\fShift+\\f-\\fStart,%d) {\\f};\n", rank, color, height++));
1496         PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1497       }
1498 
1499       color = colors[1 % numColors];
1500       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1501       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1502       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1503       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,%d) {\\e};\n", rank, color, height++));
1504       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1505 
1506       color = colors[0 % numColors];
1507       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1508       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1509       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1510       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,%d) {\\v};\n", rank, color, height++));
1511       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1512 
1513       for (p = pStart; p < pEnd; ++p) {
1514         const PetscInt *cone;
1515         PetscInt        coneSize, cp;
1516 
1517         PetscCall(DMPlexGetCone(dm, p, &cone));
1518         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1519         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1520       }
1521     }
1522     PetscCall(PetscViewerFlush(viewer));
1523     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1524     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1525     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1526     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1527     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1528     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1529     PetscCall(PetscFree3(names, colors, lcolors));
1530     PetscCall(PetscBTDestroy(&wp));
1531   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1532     Vec                    cown, acown;
1533     VecScatter             sct;
1534     ISLocalToGlobalMapping g2l;
1535     IS                     gid, acis;
1536     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1537     MPI_Group              ggroup, ngroup;
1538     PetscScalar           *array, nid;
1539     const PetscInt        *idxs;
1540     PetscInt              *idxs2, *start, *adjacency, *work;
1541     PetscInt64             lm[3], gm[3];
1542     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1543     PetscMPIInt            d1, d2, rank;
1544 
1545     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1546     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1547 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1548     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1549 #endif
1550     if (ncomm != MPI_COMM_NULL) {
1551       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1552       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1553       d1 = 0;
1554       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1555       nid = d2;
1556       PetscCallMPI(MPI_Group_free(&ggroup));
1557       PetscCallMPI(MPI_Group_free(&ngroup));
1558       PetscCallMPI(MPI_Comm_free(&ncomm));
1559     } else nid = 0.0;
1560 
1561     /* Get connectivity */
1562     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1563     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1564 
1565     /* filter overlapped local cells */
1566     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1567     PetscCall(ISGetIndices(gid, &idxs));
1568     PetscCall(ISGetLocalSize(gid, &cum));
1569     PetscCall(PetscMalloc1(cum, &idxs2));
1570     for (c = cStart, cum = 0; c < cEnd; c++) {
1571       if (idxs[c - cStart] < 0) continue;
1572       idxs2[cum++] = idxs[c - cStart];
1573     }
1574     PetscCall(ISRestoreIndices(gid, &idxs));
1575     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1576     PetscCall(ISDestroy(&gid));
1577     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1578 
1579     /* support for node-aware cell locality */
1580     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1581     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1582     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1583     PetscCall(VecGetArray(cown, &array));
1584     for (c = 0; c < numVertices; c++) array[c] = nid;
1585     PetscCall(VecRestoreArray(cown, &array));
1586     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1587     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1588     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1589     PetscCall(ISDestroy(&acis));
1590     PetscCall(VecScatterDestroy(&sct));
1591     PetscCall(VecDestroy(&cown));
1592 
1593     /* compute edgeCut */
1594     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1595     PetscCall(PetscMalloc1(cum, &work));
1596     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1597     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1598     PetscCall(ISDestroy(&gid));
1599     PetscCall(VecGetArray(acown, &array));
1600     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1601       PetscInt totl;
1602 
1603       totl = start[c + 1] - start[c];
1604       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1605       for (i = 0; i < totl; i++) {
1606         if (work[i] < 0) {
1607           ect += 1;
1608           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1609         }
1610       }
1611     }
1612     PetscCall(PetscFree(work));
1613     PetscCall(VecRestoreArray(acown, &array));
1614     lm[0] = numVertices > 0 ? numVertices : PETSC_INT_MAX;
1615     lm[1] = -numVertices;
1616     PetscCallMPI(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1617     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt64_FMT ", min %" PetscInt64_FMT, -((double)gm[1]) / ((double)gm[0]), -gm[1], gm[0]));
1618     lm[0] = ect;                     /* edgeCut */
1619     lm[1] = ectn;                    /* node-aware edgeCut */
1620     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1621     PetscCallMPI(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1622     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt64_FMT ")\n", gm[2]));
1623 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1624     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt64_FMT " (on node %.3f)\n", gm[0] / 2, gm[0] ? ((double)gm[1]) / ((double)gm[0]) : 1.));
1625 #else
1626     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt64_FMT " (on node %.3f)\n", gm[0] / 2, 0.0));
1627 #endif
1628     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1629     PetscCall(PetscFree(start));
1630     PetscCall(PetscFree(adjacency));
1631     PetscCall(VecDestroy(&acown));
1632   } else {
1633     const char    *name;
1634     PetscInt      *sizes, *hybsizes, *ghostsizes;
1635     PetscInt       locDepth, depth, cellHeight, dim, d;
1636     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1637     PetscInt       numLabels, l, maxSize = 17;
1638     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1639     MPI_Comm       comm;
1640     PetscMPIInt    size, rank;
1641 
1642     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1643     PetscCallMPI(MPI_Comm_size(comm, &size));
1644     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1645     PetscCall(DMGetDimension(dm, &dim));
1646     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1647     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1648     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1649     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1650     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1651     PetscCall(DMPlexGetDepth(dm, &locDepth));
1652     PetscCallMPI(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1653     PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &gcStart, &gcEnd));
1654     gcNum = gcEnd - gcStart;
1655     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1656     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1657     for (d = 0; d <= depth; d++) {
1658       PetscInt Nc[2] = {0, 0}, ict;
1659 
1660       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1661       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1662       ict = ct0;
1663       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1664       ct0 = (DMPolytopeType)ict;
1665       for (p = pStart; p < pEnd; ++p) {
1666         DMPolytopeType ct;
1667 
1668         PetscCall(DMPlexGetCellType(dm, p, &ct));
1669         if (ct == ct0) ++Nc[0];
1670         else ++Nc[1];
1671       }
1672       if (size < maxSize) {
1673         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1674         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1675         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1676         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1677         for (p = 0; p < size; ++p) {
1678           if (rank == 0) {
1679             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1680             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1681             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1682           }
1683         }
1684       } else {
1685         PetscInt locMinMax[2];
1686 
1687         locMinMax[0] = Nc[0] + Nc[1];
1688         locMinMax[1] = Nc[0] + Nc[1];
1689         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1690         locMinMax[0] = Nc[1];
1691         locMinMax[1] = Nc[1];
1692         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1693         if (d == depth) {
1694           locMinMax[0] = gcNum;
1695           locMinMax[1] = gcNum;
1696           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1697         }
1698         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1699         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1700         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1701         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1702       }
1703       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1704     }
1705     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1706     {
1707       const PetscReal *maxCell;
1708       const PetscReal *L;
1709       PetscBool        localized;
1710 
1711       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1712       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1713       if (L || localized) {
1714         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1715         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1716         if (L) {
1717           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1718           for (d = 0; d < dim; ++d) {
1719             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1720             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1721           }
1722           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1723         }
1724         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1725         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1726       }
1727     }
1728     PetscCall(DMGetNumLabels(dm, &numLabels));
1729     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1730     for (l = 0; l < numLabels; ++l) {
1731       DMLabel     label;
1732       const char *name;
1733       PetscInt   *values;
1734       PetscInt    numValues, v;
1735 
1736       PetscCall(DMGetLabelName(dm, l, &name));
1737       PetscCall(DMGetLabel(dm, name, &label));
1738       PetscCall(DMLabelGetNumValues(label, &numValues));
1739       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1740 
1741       { // Extract array of DMLabel values so it can be sorted
1742         IS              is_values;
1743         const PetscInt *is_values_local = NULL;
1744 
1745         PetscCall(DMLabelGetValueIS(label, &is_values));
1746         PetscCall(ISGetIndices(is_values, &is_values_local));
1747         PetscCall(PetscMalloc1(numValues, &values));
1748         PetscCall(PetscArraycpy(values, is_values_local, numValues));
1749         PetscCall(PetscSortInt(numValues, values));
1750         PetscCall(ISRestoreIndices(is_values, &is_values_local));
1751         PetscCall(ISDestroy(&is_values));
1752       }
1753       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1754       for (v = 0; v < numValues; ++v) {
1755         PetscInt size;
1756 
1757         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1758         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1759         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1760       }
1761       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1762       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1763       PetscCall(PetscFree(values));
1764     }
1765     {
1766       char    **labelNames;
1767       PetscInt  Nl = numLabels;
1768       PetscBool flg;
1769 
1770       PetscCall(PetscMalloc1(Nl, &labelNames));
1771       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1772       for (l = 0; l < Nl; ++l) {
1773         DMLabel label;
1774 
1775         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1776         if (flg) {
1777           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1778           PetscCall(DMLabelView(label, viewer));
1779         }
1780         PetscCall(PetscFree(labelNames[l]));
1781       }
1782       PetscCall(PetscFree(labelNames));
1783     }
1784     /* If no fields are specified, people do not want to see adjacency */
1785     if (dm->Nf) {
1786       PetscInt f;
1787 
1788       for (f = 0; f < dm->Nf; ++f) {
1789         const char *name;
1790 
1791         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1792         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1793         PetscCall(PetscViewerASCIIPushTab(viewer));
1794         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1795         if (dm->fields[f].adjacency[0]) {
1796           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1797           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1798         } else {
1799           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1800           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1801         }
1802         PetscCall(PetscViewerASCIIPopTab(viewer));
1803       }
1804     }
1805     DMPlexTransform tr;
1806 
1807     PetscCall(DMPlexGetTransform(dm, &tr));
1808     if (tr) {
1809       PetscCall(PetscViewerASCIIPushTab(viewer));
1810       PetscCall(PetscViewerASCIIPrintf(viewer, "Created using transform:\n"));
1811       PetscCall(DMPlexTransformView(tr, viewer));
1812       PetscCall(PetscViewerASCIIPopTab(viewer));
1813     }
1814     PetscCall(DMGetCoarseDM(dm, &cdm));
1815     if (cdm) {
1816       PetscCall(PetscViewerASCIIPushTab(viewer));
1817       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1818       PetscCall(DMPlexView_Ascii(cdm, viewer));
1819       PetscCall(PetscViewerASCIIPopTab(viewer));
1820     }
1821   }
1822   PetscFunctionReturn(PETSC_SUCCESS);
1823 }
1824 
1825 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt lC, PetscInt cC, PetscInt cell, const PetscScalar coords[])
1826 {
1827   DMPolytopeType ct;
1828   PetscMPIInt    rank;
1829   PetscInt       cdim;
1830   int            lineColor, cellColor;
1831 
1832   PetscFunctionBegin;
1833   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1834   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1835   PetscCall(DMGetCoordinateDim(dm, &cdim));
1836   lineColor = (int)(lC < 0 ? PETSC_DRAW_BLACK : lC);
1837   cellColor = (int)(cC < 0 ? PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2 : cC);
1838   switch (ct) {
1839   case DM_POLYTOPE_SEGMENT:
1840   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1841     switch (cdim) {
1842     case 1: {
1843       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1844       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1845 
1846       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, lineColor));
1847       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, lineColor));
1848       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, lineColor));
1849     } break;
1850     case 2: {
1851       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1852       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1853       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1854 
1855       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), lineColor));
1856       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]) + l * dx, PetscRealPart(coords[1]) + l * dy, PetscRealPart(coords[0]) - l * dx, PetscRealPart(coords[1]) - l * dy, lineColor));
1857       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]) + l * dx, PetscRealPart(coords[3]) + l * dy, PetscRealPart(coords[2]) - l * dx, PetscRealPart(coords[3]) - l * dy, lineColor));
1858     } break;
1859     default:
1860       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1861     }
1862     break;
1863   case DM_POLYTOPE_TRIANGLE:
1864     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), cellColor, cellColor, cellColor));
1865     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), lineColor));
1866     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), lineColor));
1867     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), lineColor));
1868     break;
1869   case DM_POLYTOPE_QUADRILATERAL:
1870     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), cellColor, cellColor, cellColor));
1871     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), cellColor, cellColor, cellColor));
1872     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), lineColor));
1873     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), lineColor));
1874     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), lineColor));
1875     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), lineColor));
1876     break;
1877   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1878     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), cellColor, cellColor, cellColor));
1879     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), cellColor, cellColor, cellColor));
1880     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), lineColor));
1881     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), lineColor));
1882     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), lineColor));
1883     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), lineColor));
1884     break;
1885   case DM_POLYTOPE_FV_GHOST:
1886     break;
1887   default:
1888     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1889   }
1890   PetscFunctionReturn(PETSC_SUCCESS);
1891 }
1892 
1893 static PetscErrorCode DrawPolygon_Private(DM dm, PetscDraw draw, PetscInt cell, PetscInt Nv, const PetscReal refVertices[], const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1894 {
1895   PetscReal   centroid[2] = {0., 0.};
1896   PetscMPIInt rank;
1897   PetscMPIInt fillColor;
1898 
1899   PetscFunctionBegin;
1900   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1901   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1902   for (PetscInt v = 0; v < Nv; ++v) {
1903     centroid[0] += PetscRealPart(coords[v * 2 + 0]) / Nv;
1904     centroid[1] += PetscRealPart(coords[v * 2 + 1]) / Nv;
1905   }
1906   for (PetscInt e = 0; e < Nv; ++e) {
1907     refCoords[0] = refVertices[e * 2 + 0];
1908     refCoords[1] = refVertices[e * 2 + 1];
1909     for (PetscInt d = 1; d <= edgeDiv; ++d) {
1910       refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % Nv * 2 + 0] - refCoords[0]) * d / edgeDiv;
1911       refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % Nv * 2 + 1] - refCoords[1]) * d / edgeDiv;
1912     }
1913     PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1914     for (PetscInt d = 0; d < edgeDiv; ++d) {
1915       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));
1916       PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1917     }
1918   }
1919   PetscFunctionReturn(PETSC_SUCCESS);
1920 }
1921 
1922 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1923 {
1924   DMPolytopeType ct;
1925 
1926   PetscFunctionBegin;
1927   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1928   switch (ct) {
1929   case DM_POLYTOPE_TRIANGLE: {
1930     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1931 
1932     PetscCall(DrawPolygon_Private(dm, draw, cell, 3, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1933   } break;
1934   case DM_POLYTOPE_QUADRILATERAL: {
1935     PetscReal refVertices[8] = {-1., -1., 1., -1., 1., 1., -1., 1.};
1936 
1937     PetscCall(DrawPolygon_Private(dm, draw, cell, 4, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1938   } break;
1939   default:
1940     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1941   }
1942   PetscFunctionReturn(PETSC_SUCCESS);
1943 }
1944 
1945 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1946 {
1947   PetscDraw    draw;
1948   DM           cdm;
1949   PetscSection coordSection;
1950   Vec          coordinates;
1951   PetscReal    xyl[3], xyr[3];
1952   PetscReal   *refCoords, *edgeCoords;
1953   PetscBool    isnull, drawAffine;
1954   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, cDegree, edgeDiv, lineColor = PETSC_DETERMINE, cellColor = PETSC_DETERMINE;
1955 
1956   PetscFunctionBegin;
1957   PetscCall(DMGetCoordinateDim(dm, &dim));
1958   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1959   PetscCall(DMGetCoordinateDegree_Internal(dm, &cDegree));
1960   drawAffine = cDegree > 1 ? PETSC_FALSE : PETSC_TRUE;
1961   edgeDiv    = cDegree + 1;
1962   PetscCall(PetscOptionsGetInt(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_line_color", &lineColor, NULL));
1963   PetscCall(PetscOptionsGetInt(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_cell_color", &cellColor, NULL));
1964   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1965   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1966   PetscCall(DMGetCoordinateDM(dm, &cdm));
1967   PetscCall(DMGetLocalSection(cdm, &coordSection));
1968   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1969   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1970   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1971 
1972   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1973   PetscCall(PetscDrawIsNull(draw, &isnull));
1974   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1975   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1976 
1977   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1978   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1979   PetscCall(PetscDrawClear(draw));
1980 
1981   for (c = cStart; c < cEnd; ++c) {
1982     PetscScalar       *coords = NULL;
1983     const PetscScalar *coords_arr;
1984     PetscInt           numCoords;
1985     PetscBool          isDG;
1986 
1987     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1988     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, lineColor, cellColor, c, coords));
1989     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1990     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1991   }
1992   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1993   PetscCall(PetscDrawFlush(draw));
1994   PetscCall(PetscDrawPause(draw));
1995   PetscCall(PetscDrawSave(draw));
1996   PetscFunctionReturn(PETSC_SUCCESS);
1997 }
1998 
1999 static PetscErrorCode DMPlexCreateHighOrderSurrogate_Internal(DM dm, DM *hdm)
2000 {
2001   DM           odm = dm, rdm = dm, cdm;
2002   PetscFE      fe;
2003   PetscSpace   sp;
2004   PetscClassId id;
2005   PetscInt     degree;
2006   PetscBool    hoView = PETSC_TRUE;
2007 
2008   PetscFunctionBegin;
2009   PetscObjectOptionsBegin((PetscObject)dm);
2010   PetscCall(PetscOptionsBool("-dm_plex_high_order_view", "Subsample to view meshes with high order coordinates", "DMPlexCreateHighOrderSurrogate_Internal", hoView, &hoView, NULL));
2011   PetscOptionsEnd();
2012   PetscCall(PetscObjectReference((PetscObject)dm));
2013   *hdm = dm;
2014   if (!hoView) PetscFunctionReturn(PETSC_SUCCESS);
2015   PetscCall(DMGetCoordinateDM(dm, &cdm));
2016   PetscCall(DMGetField(cdm, 0, NULL, (PetscObject *)&fe));
2017   PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
2018   if (id != PETSCFE_CLASSID) PetscFunctionReturn(PETSC_SUCCESS);
2019   PetscCall(PetscFEGetBasisSpace(fe, &sp));
2020   PetscCall(PetscSpaceGetDegree(sp, &degree, NULL));
2021   for (PetscInt r = 0, rd = PetscCeilReal(((PetscReal)degree) / 2.); r < (PetscInt)PetscCeilReal(PetscLog2Real(degree)); ++r, rd = PetscCeilReal(((PetscReal)rd) / 2.)) {
2022     DM  cdm, rcdm;
2023     Mat In;
2024     Vec cl, rcl;
2025 
2026     PetscCall(DMRefine(odm, PetscObjectComm((PetscObject)odm), &rdm));
2027     PetscCall(DMPlexCreateCoordinateSpace(rdm, rd, PETSC_FALSE, NULL));
2028     PetscCall(PetscObjectSetName((PetscObject)rdm, "Refined Mesh with Linear Coordinates"));
2029     PetscCall(DMGetCoordinateDM(odm, &cdm));
2030     PetscCall(DMGetCoordinateDM(rdm, &rcdm));
2031     PetscCall(DMGetCoordinatesLocal(odm, &cl));
2032     PetscCall(DMGetCoordinatesLocal(rdm, &rcl));
2033     PetscCall(DMSetCoarseDM(rcdm, cdm));
2034     PetscCall(DMCreateInterpolation(cdm, rcdm, &In, NULL));
2035     PetscCall(MatMult(In, cl, rcl));
2036     PetscCall(MatDestroy(&In));
2037     PetscCall(DMSetCoordinatesLocal(rdm, rcl));
2038     PetscCall(DMDestroy(&odm));
2039     odm = rdm;
2040   }
2041   *hdm = rdm;
2042   PetscFunctionReturn(PETSC_SUCCESS);
2043 }
2044 
2045 #if defined(PETSC_HAVE_EXODUSII)
2046   #include <exodusII.h>
2047   #include <petscviewerexodusii.h>
2048 #endif
2049 
2050 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
2051 {
2052   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns, ispython;
2053   char      name[PETSC_MAX_PATH_LEN];
2054 
2055   PetscFunctionBegin;
2056   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2057   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2058   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
2059   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
2060   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2061   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
2062   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
2063   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
2064   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
2065   PetscCall(PetscObjectHasFunction((PetscObject)viewer, "PetscViewerPythonViewObject_C", &ispython));
2066   if (iascii) {
2067     PetscViewerFormat format;
2068     PetscCall(PetscViewerGetFormat(viewer, &format));
2069     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
2070     else PetscCall(DMPlexView_Ascii(dm, viewer));
2071   } else if (ishdf5) {
2072 #if defined(PETSC_HAVE_HDF5)
2073     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
2074 #else
2075     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2076 #endif
2077   } else if (isvtk) {
2078     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
2079   } else if (isdraw) {
2080     DM hdm;
2081 
2082     PetscCall(DMPlexCreateHighOrderSurrogate_Internal(dm, &hdm));
2083     PetscCall(DMPlexView_Draw(hdm, viewer));
2084     PetscCall(DMDestroy(&hdm));
2085   } else if (isglvis) {
2086     PetscCall(DMPlexView_GLVis(dm, viewer));
2087 #if defined(PETSC_HAVE_EXODUSII)
2088   } else if (isexodus) {
2089     /*
2090       exodusII requires that all sets be part of exactly one cell set.
2091       If the dm does not have a "Cell Sets" label defined, we create one
2092       with ID 1, containing all cells.
2093       Note that if the Cell Sets label is defined but does not cover all cells,
2094       we may still have a problem. This should probably be checked here or in the viewer;
2095     */
2096     PetscInt numCS;
2097     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
2098     if (!numCS) {
2099       PetscInt cStart, cEnd, c;
2100       PetscCall(DMCreateLabel(dm, "Cell Sets"));
2101       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
2102       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
2103     }
2104     PetscCall(DMView_PlexExodusII(dm, viewer));
2105 #endif
2106 #if defined(PETSC_HAVE_CGNS)
2107   } else if (iscgns) {
2108     PetscCall(DMView_PlexCGNS(dm, viewer));
2109 #endif
2110   } else if (ispython) {
2111     PetscCall(PetscViewerPythonViewObject(viewer, (PetscObject)dm));
2112   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
2113   /* Optionally view the partition */
2114   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
2115   if (flg) {
2116     Vec ranks;
2117     PetscCall(DMPlexCreateRankField(dm, &ranks));
2118     PetscCall(VecView(ranks, viewer));
2119     PetscCall(VecDestroy(&ranks));
2120   }
2121   /* Optionally view a label */
2122   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
2123   if (flg) {
2124     DMLabel label;
2125     Vec     val;
2126 
2127     PetscCall(DMGetLabel(dm, name, &label));
2128     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
2129     PetscCall(DMPlexCreateLabelField(dm, label, &val));
2130     PetscCall(VecView(val, viewer));
2131     PetscCall(VecDestroy(&val));
2132   }
2133   PetscFunctionReturn(PETSC_SUCCESS);
2134 }
2135 
2136 /*@
2137   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
2138 
2139   Collective
2140 
2141   Input Parameters:
2142 + dm     - The `DM` whose topology is to be saved
2143 - viewer - The `PetscViewer` to save it in
2144 
2145   Level: advanced
2146 
2147 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
2148 @*/
2149 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
2150 {
2151   PetscBool ishdf5;
2152 
2153   PetscFunctionBegin;
2154   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2155   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2156   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2157   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
2158   if (ishdf5) {
2159 #if defined(PETSC_HAVE_HDF5)
2160     PetscViewerFormat format;
2161     PetscCall(PetscViewerGetFormat(viewer, &format));
2162     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2163       IS globalPointNumbering;
2164 
2165       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2166       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
2167       PetscCall(ISDestroy(&globalPointNumbering));
2168     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2169 #else
2170     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2171 #endif
2172   }
2173   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
2174   PetscFunctionReturn(PETSC_SUCCESS);
2175 }
2176 
2177 /*@
2178   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
2179 
2180   Collective
2181 
2182   Input Parameters:
2183 + dm     - The `DM` whose coordinates are to be saved
2184 - viewer - The `PetscViewer` for saving
2185 
2186   Level: advanced
2187 
2188 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
2189 @*/
2190 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
2191 {
2192   PetscBool ishdf5;
2193 
2194   PetscFunctionBegin;
2195   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2196   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2197   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2198   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2199   if (ishdf5) {
2200 #if defined(PETSC_HAVE_HDF5)
2201     PetscViewerFormat format;
2202     PetscCall(PetscViewerGetFormat(viewer, &format));
2203     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2204       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
2205     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2206 #else
2207     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2208 #endif
2209   }
2210   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2211   PetscFunctionReturn(PETSC_SUCCESS);
2212 }
2213 
2214 /*@
2215   DMPlexLabelsView - Saves `DMPLEX` labels into a file
2216 
2217   Collective
2218 
2219   Input Parameters:
2220 + dm     - The `DM` whose labels are to be saved
2221 - viewer - The `PetscViewer` for saving
2222 
2223   Level: advanced
2224 
2225 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
2226 @*/
2227 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
2228 {
2229   PetscBool ishdf5;
2230 
2231   PetscFunctionBegin;
2232   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2233   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2234   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2235   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
2236   if (ishdf5) {
2237 #if defined(PETSC_HAVE_HDF5)
2238     IS                globalPointNumbering;
2239     PetscViewerFormat format;
2240 
2241     PetscCall(PetscViewerGetFormat(viewer, &format));
2242     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2243       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2244       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
2245       PetscCall(ISDestroy(&globalPointNumbering));
2246     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2247 #else
2248     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2249 #endif
2250   }
2251   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
2252   PetscFunctionReturn(PETSC_SUCCESS);
2253 }
2254 
2255 /*@
2256   DMPlexSectionView - Saves a section associated with a `DMPLEX`
2257 
2258   Collective
2259 
2260   Input Parameters:
2261 + dm        - The `DM` that contains the topology on which the section to be saved is defined
2262 . viewer    - The `PetscViewer` for saving
2263 - sectiondm - The `DM` that contains the section to be saved, can be `NULL`
2264 
2265   Level: advanced
2266 
2267   Notes:
2268   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.
2269 
2270   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.
2271 
2272 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
2273 @*/
2274 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
2275 {
2276   PetscBool ishdf5;
2277 
2278   PetscFunctionBegin;
2279   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2280   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2281   if (!sectiondm) sectiondm = dm;
2282   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2283   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2284   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
2285   if (ishdf5) {
2286 #if defined(PETSC_HAVE_HDF5)
2287     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
2288 #else
2289     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2290 #endif
2291   }
2292   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
2293   PetscFunctionReturn(PETSC_SUCCESS);
2294 }
2295 
2296 /*@
2297   DMPlexGlobalVectorView - Saves a global vector
2298 
2299   Collective
2300 
2301   Input Parameters:
2302 + dm        - The `DM` that represents the topology
2303 . viewer    - The `PetscViewer` to save data with
2304 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2305 - vec       - The global vector to be saved
2306 
2307   Level: advanced
2308 
2309   Notes:
2310   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2311 
2312   Calling sequence:
2313 .vb
2314        DMCreate(PETSC_COMM_WORLD, &dm);
2315        DMSetType(dm, DMPLEX);
2316        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2317        DMClone(dm, &sectiondm);
2318        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2319        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2320        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2321        PetscSectionSetChart(section, pStart, pEnd);
2322        PetscSectionSetUp(section);
2323        DMSetLocalSection(sectiondm, section);
2324        PetscSectionDestroy(&section);
2325        DMGetGlobalVector(sectiondm, &vec);
2326        PetscObjectSetName((PetscObject)vec, "vec_name");
2327        DMPlexTopologyView(dm, viewer);
2328        DMPlexSectionView(dm, viewer, sectiondm);
2329        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2330        DMRestoreGlobalVector(sectiondm, &vec);
2331        DMDestroy(&sectiondm);
2332        DMDestroy(&dm);
2333 .ve
2334 
2335 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2336 @*/
2337 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2338 {
2339   PetscBool ishdf5;
2340 
2341   PetscFunctionBegin;
2342   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2343   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2344   if (!sectiondm) sectiondm = dm;
2345   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2346   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2347   /* Check consistency */
2348   {
2349     PetscSection section;
2350     PetscBool    includesConstraints;
2351     PetscInt     m, m1;
2352 
2353     PetscCall(VecGetLocalSize(vec, &m1));
2354     PetscCall(DMGetGlobalSection(sectiondm, &section));
2355     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2356     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2357     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2358     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2359   }
2360   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2361   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2362   if (ishdf5) {
2363 #if defined(PETSC_HAVE_HDF5)
2364     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2365 #else
2366     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2367 #endif
2368   }
2369   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2370   PetscFunctionReturn(PETSC_SUCCESS);
2371 }
2372 
2373 /*@
2374   DMPlexLocalVectorView - Saves a local vector
2375 
2376   Collective
2377 
2378   Input Parameters:
2379 + dm        - The `DM` that represents the topology
2380 . viewer    - The `PetscViewer` to save data with
2381 . sectiondm - The `DM` that contains the local section on which `vec` is defined, can be `NULL`
2382 - vec       - The local vector to be saved
2383 
2384   Level: advanced
2385 
2386   Note:
2387   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.
2388 
2389   Calling sequence:
2390 .vb
2391        DMCreate(PETSC_COMM_WORLD, &dm);
2392        DMSetType(dm, DMPLEX);
2393        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2394        DMClone(dm, &sectiondm);
2395        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2396        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2397        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2398        PetscSectionSetChart(section, pStart, pEnd);
2399        PetscSectionSetUp(section);
2400        DMSetLocalSection(sectiondm, section);
2401        DMGetLocalVector(sectiondm, &vec);
2402        PetscObjectSetName((PetscObject)vec, "vec_name");
2403        DMPlexTopologyView(dm, viewer);
2404        DMPlexSectionView(dm, viewer, sectiondm);
2405        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2406        DMRestoreLocalVector(sectiondm, &vec);
2407        DMDestroy(&sectiondm);
2408        DMDestroy(&dm);
2409 .ve
2410 
2411 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2412 @*/
2413 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2414 {
2415   PetscBool ishdf5;
2416 
2417   PetscFunctionBegin;
2418   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2419   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2420   if (!sectiondm) sectiondm = dm;
2421   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2422   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2423   /* Check consistency */
2424   {
2425     PetscSection section;
2426     PetscBool    includesConstraints;
2427     PetscInt     m, m1;
2428 
2429     PetscCall(VecGetLocalSize(vec, &m1));
2430     PetscCall(DMGetLocalSection(sectiondm, &section));
2431     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2432     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2433     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2434     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2435   }
2436   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2437   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2438   if (ishdf5) {
2439 #if defined(PETSC_HAVE_HDF5)
2440     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2441 #else
2442     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2443 #endif
2444   }
2445   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2446   PetscFunctionReturn(PETSC_SUCCESS);
2447 }
2448 
2449 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2450 {
2451   PetscBool ishdf5;
2452 
2453   PetscFunctionBegin;
2454   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2455   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2456   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2457   if (ishdf5) {
2458 #if defined(PETSC_HAVE_HDF5)
2459     PetscViewerFormat format;
2460     PetscCall(PetscViewerGetFormat(viewer, &format));
2461     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2462       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2463     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2464       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2465     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2466     PetscFunctionReturn(PETSC_SUCCESS);
2467 #else
2468     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2469 #endif
2470   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2471 }
2472 
2473 /*@
2474   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2475 
2476   Collective
2477 
2478   Input Parameters:
2479 + dm     - The `DM` into which the topology is loaded
2480 - viewer - The `PetscViewer` for the saved topology
2481 
2482   Output Parameter:
2483 . 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;
2484   `NULL` if unneeded
2485 
2486   Level: advanced
2487 
2488 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2489           `PetscViewer`, `PetscSF`
2490 @*/
2491 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2492 {
2493   PetscBool ishdf5;
2494 
2495   PetscFunctionBegin;
2496   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2497   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2498   if (globalToLocalPointSF) PetscAssertPointer(globalToLocalPointSF, 3);
2499   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2500   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2501   if (ishdf5) {
2502 #if defined(PETSC_HAVE_HDF5)
2503     PetscViewerFormat format;
2504     PetscCall(PetscViewerGetFormat(viewer, &format));
2505     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2506       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2507     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2508 #else
2509     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2510 #endif
2511   }
2512   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2513   PetscFunctionReturn(PETSC_SUCCESS);
2514 }
2515 
2516 /*@
2517   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2518 
2519   Collective
2520 
2521   Input Parameters:
2522 + dm                   - The `DM` into which the coordinates are loaded
2523 . viewer               - The `PetscViewer` for the saved coordinates
2524 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2525 
2526   Level: advanced
2527 
2528 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2529           `PetscSF`, `PetscViewer`
2530 @*/
2531 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2532 {
2533   PetscBool ishdf5;
2534 
2535   PetscFunctionBegin;
2536   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2537   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2538   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2539   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2540   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2541   if (ishdf5) {
2542 #if defined(PETSC_HAVE_HDF5)
2543     PetscViewerFormat format;
2544     PetscCall(PetscViewerGetFormat(viewer, &format));
2545     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2546       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2547     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2548 #else
2549     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2550 #endif
2551   }
2552   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2553   PetscFunctionReturn(PETSC_SUCCESS);
2554 }
2555 
2556 /*@
2557   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2558 
2559   Collective
2560 
2561   Input Parameters:
2562 + dm                   - The `DM` into which the labels are loaded
2563 . viewer               - The `PetscViewer` for the saved labels
2564 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2565 
2566   Level: advanced
2567 
2568   Note:
2569   The `PetscSF` argument must not be `NULL` if the `DM` is distributed, otherwise an error occurs.
2570 
2571 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2572           `PetscSF`, `PetscViewer`
2573 @*/
2574 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2575 {
2576   PetscBool ishdf5;
2577 
2578   PetscFunctionBegin;
2579   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2580   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2581   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2582   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2583   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2584   if (ishdf5) {
2585 #if defined(PETSC_HAVE_HDF5)
2586     PetscViewerFormat format;
2587 
2588     PetscCall(PetscViewerGetFormat(viewer, &format));
2589     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2590       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2591     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2592 #else
2593     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2594 #endif
2595   }
2596   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2597   PetscFunctionReturn(PETSC_SUCCESS);
2598 }
2599 
2600 /*@
2601   DMPlexSectionLoad - Loads section into a `DMPLEX`
2602 
2603   Collective
2604 
2605   Input Parameters:
2606 + dm                   - The `DM` that represents the topology
2607 . viewer               - The `PetscViewer` that represents the on-disk section (sectionA)
2608 . sectiondm            - The `DM` into which the on-disk section (sectionA) is migrated, can be `NULL`
2609 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2610 
2611   Output Parameters:
2612 + 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)
2613 - 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)
2614 
2615   Level: advanced
2616 
2617   Notes:
2618   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.
2619 
2620   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.
2621 
2622   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.
2623 
2624   Example using 2 processes:
2625 .vb
2626   NX (number of points on dm): 4
2627   sectionA                   : the on-disk section
2628   vecA                       : a vector associated with sectionA
2629   sectionB                   : sectiondm's local section constructed in this function
2630   vecB (local)               : a vector associated with sectiondm's local section
2631   vecB (global)              : a vector associated with sectiondm's global section
2632 
2633                                      rank 0    rank 1
2634   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2635   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2636   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2637   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2638   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2639   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2640   sectionB->atlasDof             :     1 0 1 | 1 3
2641   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2642   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2643   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2644 .ve
2645   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2646 
2647 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2648 @*/
2649 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, PeOp DM sectiondm, PetscSF globalToLocalPointSF, PeOp PetscSF *globalDofSF, PeOp PetscSF *localDofSF)
2650 {
2651   PetscBool ishdf5;
2652 
2653   PetscFunctionBegin;
2654   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2655   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2656   if (!sectiondm) sectiondm = dm;
2657   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2658   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2659   if (globalDofSF) PetscAssertPointer(globalDofSF, 5);
2660   if (localDofSF) PetscAssertPointer(localDofSF, 6);
2661   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2662   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2663   if (ishdf5) {
2664 #if defined(PETSC_HAVE_HDF5)
2665     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2666 #else
2667     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2668 #endif
2669   }
2670   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2671   PetscFunctionReturn(PETSC_SUCCESS);
2672 }
2673 
2674 /*@
2675   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2676 
2677   Collective
2678 
2679   Input Parameters:
2680 + dm        - The `DM` that represents the topology
2681 . viewer    - The `PetscViewer` that represents the on-disk vector data
2682 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2683 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2684 - vec       - The global vector to set values of
2685 
2686   Level: advanced
2687 
2688   Notes:
2689   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.
2690 
2691   Calling sequence:
2692 .vb
2693        DMCreate(PETSC_COMM_WORLD, &dm);
2694        DMSetType(dm, DMPLEX);
2695        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2696        DMPlexTopologyLoad(dm, viewer, &sfX);
2697        DMClone(dm, &sectiondm);
2698        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2699        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2700        DMGetGlobalVector(sectiondm, &vec);
2701        PetscObjectSetName((PetscObject)vec, "vec_name");
2702        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2703        DMRestoreGlobalVector(sectiondm, &vec);
2704        PetscSFDestroy(&gsf);
2705        PetscSFDestroy(&sfX);
2706        DMDestroy(&sectiondm);
2707        DMDestroy(&dm);
2708 .ve
2709 
2710 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2711           `PetscSF`, `PetscViewer`
2712 @*/
2713 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2714 {
2715   PetscBool ishdf5;
2716 
2717   PetscFunctionBegin;
2718   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2719   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2720   if (!sectiondm) sectiondm = dm;
2721   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2722   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2723   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2724   /* Check consistency */
2725   {
2726     PetscSection section;
2727     PetscBool    includesConstraints;
2728     PetscInt     m, m1;
2729 
2730     PetscCall(VecGetLocalSize(vec, &m1));
2731     PetscCall(DMGetGlobalSection(sectiondm, &section));
2732     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2733     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2734     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2735     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2736   }
2737   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2738   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2739   if (ishdf5) {
2740 #if defined(PETSC_HAVE_HDF5)
2741     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2742 #else
2743     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2744 #endif
2745   }
2746   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2747   PetscFunctionReturn(PETSC_SUCCESS);
2748 }
2749 
2750 /*@
2751   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2752 
2753   Collective
2754 
2755   Input Parameters:
2756 + dm        - The `DM` that represents the topology
2757 . viewer    - The `PetscViewer` that represents the on-disk vector data
2758 . sectiondm - The `DM` that contains the local section on which vec is defined, can be `NULL`
2759 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2760 - vec       - The local vector to set values of
2761 
2762   Level: advanced
2763 
2764   Notes:
2765   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.
2766 
2767   Calling sequence:
2768 .vb
2769        DMCreate(PETSC_COMM_WORLD, &dm);
2770        DMSetType(dm, DMPLEX);
2771        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2772        DMPlexTopologyLoad(dm, viewer, &sfX);
2773        DMClone(dm, &sectiondm);
2774        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2775        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2776        DMGetLocalVector(sectiondm, &vec);
2777        PetscObjectSetName((PetscObject)vec, "vec_name");
2778        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2779        DMRestoreLocalVector(sectiondm, &vec);
2780        PetscSFDestroy(&lsf);
2781        PetscSFDestroy(&sfX);
2782        DMDestroy(&sectiondm);
2783        DMDestroy(&dm);
2784 .ve
2785 
2786 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2787           `PetscSF`, `PetscViewer`
2788 @*/
2789 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2790 {
2791   PetscBool ishdf5;
2792 
2793   PetscFunctionBegin;
2794   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2795   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2796   if (!sectiondm) sectiondm = dm;
2797   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2798   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2799   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2800   /* Check consistency */
2801   {
2802     PetscSection section;
2803     PetscBool    includesConstraints;
2804     PetscInt     m, m1;
2805 
2806     PetscCall(VecGetLocalSize(vec, &m1));
2807     PetscCall(DMGetLocalSection(sectiondm, &section));
2808     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2809     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2810     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2811     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2812   }
2813   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2814   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2815   if (ishdf5) {
2816 #if defined(PETSC_HAVE_HDF5)
2817     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2818 #else
2819     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2820 #endif
2821   }
2822   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2823   PetscFunctionReturn(PETSC_SUCCESS);
2824 }
2825 
2826 PetscErrorCode DMDestroy_Plex(DM dm)
2827 {
2828   DM_Plex *mesh = (DM_Plex *)dm->data;
2829 
2830   PetscFunctionBegin;
2831   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2832   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2833   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", NULL));
2834   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBounds_C", NULL));
2835   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2836   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2837   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2838   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2839   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2840   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2841   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2842   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2843   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetDefault_C", NULL));
2844   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetDefault_C", NULL));
2845   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetType_C", NULL));
2846   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetType_C", NULL));
2847   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2848   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2849   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetUseCeed_C", NULL));
2850   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetUseCeed_C", NULL));
2851   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2852   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2853   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2854   PetscCall(PetscFree(mesh->cones));
2855   PetscCall(PetscFree(mesh->coneOrientations));
2856   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2857   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2858   PetscCall(PetscFree(mesh->supports));
2859   PetscCall(PetscFree(mesh->cellTypes));
2860   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2861   PetscCall(PetscFree(mesh->tetgenOpts));
2862   PetscCall(PetscFree(mesh->triangleOpts));
2863   PetscCall(PetscFree(mesh->transformType));
2864   PetscCall(PetscFree(mesh->distributionName));
2865   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2866   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2867   PetscCall(ISDestroy(&mesh->subpointIS));
2868   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2869   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2870   if (mesh->periodic.face_sfs) {
2871     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFDestroy(&mesh->periodic.face_sfs[i]));
2872     PetscCall(PetscFree(mesh->periodic.face_sfs));
2873   }
2874   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2875   if (mesh->periodic.periodic_points) {
2876     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(ISDestroy(&mesh->periodic.periodic_points[i]));
2877     PetscCall(PetscFree(mesh->periodic.periodic_points));
2878   }
2879   if (mesh->periodic.transform) PetscCall(PetscFree(mesh->periodic.transform));
2880   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2881   PetscCall(ISDestroy(&mesh->anchorIS));
2882   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2883   PetscCall(PetscFree(mesh->parents));
2884   PetscCall(PetscFree(mesh->childIDs));
2885   PetscCall(PetscSectionDestroy(&mesh->childSection));
2886   PetscCall(PetscFree(mesh->children));
2887   PetscCall(DMDestroy(&mesh->referenceTree));
2888   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2889   PetscCall(PetscFree(mesh->neighbors));
2890   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2891   if (mesh->nonempty_comm != MPI_COMM_NULL && mesh->nonempty_comm != MPI_COMM_SELF) PetscCallMPI(MPI_Comm_free(&mesh->nonempty_comm));
2892   PetscCall(DMPlexTransformDestroy(&mesh->transform));
2893   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2894   PetscCall(PetscFree(mesh));
2895   PetscFunctionReturn(PETSC_SUCCESS);
2896 }
2897 
2898 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2899 {
2900   PetscSection           sectionGlobal, sectionLocal;
2901   PetscInt               bs = -1, mbs;
2902   PetscInt               localSize, localStart = 0;
2903   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2904   MatType                mtype;
2905   ISLocalToGlobalMapping ltog;
2906 
2907   PetscFunctionBegin;
2908   PetscCall(MatInitializePackage());
2909   mtype = dm->mattype;
2910   PetscCall(DMGetLocalSection(dm, &sectionLocal));
2911   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2912   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2913   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2914   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2915   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2916   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2917   PetscCall(MatSetType(*J, mtype));
2918   PetscCall(MatSetFromOptions(*J));
2919   PetscCall(MatGetBlockSize(*J, &mbs));
2920   if (mbs > 1) bs = mbs;
2921   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2922   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2923   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2924   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2925   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2926   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2927   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2928   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2929   if (!isShell) {
2930     // There are three states with pblocks, since block starts can have no dofs:
2931     // UNKNOWN) New Block:   An open block has been signalled by pblocks[p] == 1
2932     // TRUE)    Block Start: The first entry in a block has been added
2933     // FALSE)   Block Add:   An additional block entry has been added, since pblocks[p] == 0
2934     PetscBT         blst;
2935     PetscBool3      bstate     = PETSC_BOOL3_UNKNOWN;
2936     PetscBool       fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2937     const PetscInt *perm       = NULL;
2938     PetscInt       *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2939     PetscInt        pStart, pEnd, dof, cdof, num_fields;
2940 
2941     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2942     PetscCall(PetscSectionGetBlockStarts(sectionLocal, &blst));
2943     if (sectionLocal->perm) PetscCall(ISGetIndices(sectionLocal->perm, &perm));
2944 
2945     PetscCall(PetscCalloc1(localSize, &pblocks));
2946     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2947     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2948     // We need to process in the permuted order to get block sizes right
2949     for (PetscInt point = pStart; point < pEnd; ++point) {
2950       const PetscInt p = perm ? perm[point] : point;
2951 
2952       switch (dm->blocking_type) {
2953       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2954         PetscInt bdof, offset;
2955 
2956         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2957         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2958         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2959         if (blst && PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_UNKNOWN;
2960         if (dof > 0) {
2961           // State change
2962           if (bstate == PETSC_BOOL3_UNKNOWN) bstate = PETSC_BOOL3_TRUE;
2963           else if (bstate == PETSC_BOOL3_TRUE && blst && !PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_FALSE;
2964 
2965           for (PetscInt i = 0; i < dof - cdof; ++i) pblocks[offset - localStart + i] = dof - cdof;
2966           // Signal block concatenation
2967           if (bstate == PETSC_BOOL3_FALSE && dof - cdof) pblocks[offset - localStart] = -(dof - cdof);
2968         }
2969         dof  = dof < 0 ? -(dof + 1) : dof;
2970         bdof = cdof && (dof - cdof) ? 1 : dof;
2971         if (dof) {
2972           if (bs < 0) {
2973             bs = bdof;
2974           } else if (bs != bdof) {
2975             bs = 1;
2976           }
2977         }
2978       } break;
2979       case DM_BLOCKING_FIELD_NODE: {
2980         for (PetscInt field = 0; field < num_fields; field++) {
2981           PetscInt num_comp, bdof, offset;
2982           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2983           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2984           if (dof < 0) continue;
2985           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2986           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2987           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);
2988           PetscInt num_nodes = dof / num_comp;
2989           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2990           // Handle possibly constant block size (unlikely)
2991           bdof = cdof && (dof - cdof) ? 1 : dof;
2992           if (dof) {
2993             if (bs < 0) {
2994               bs = bdof;
2995             } else if (bs != bdof) {
2996               bs = 1;
2997             }
2998           }
2999         }
3000       } break;
3001       }
3002     }
3003     if (sectionLocal->perm) PetscCall(ISRestoreIndices(sectionLocal->perm, &perm));
3004     /* Must have same blocksize on all procs (some might have no points) */
3005     bsLocal[0] = bs < 0 ? PETSC_INT_MAX : bs;
3006     bsLocal[1] = bs;
3007     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
3008     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
3009     else bs = bsMinMax[0];
3010     bs = PetscMax(1, bs);
3011     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
3012     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
3013       PetscCall(MatSetBlockSize(*J, bs));
3014       PetscCall(MatSetUp(*J));
3015     } else {
3016       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
3017       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
3018       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
3019     }
3020     if (pblocks) { // Consolidate blocks
3021       PetscInt nblocks = 0;
3022       pblocks[0]       = PetscAbs(pblocks[0]);
3023       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
3024         if (pblocks[i] == 0) continue;
3025         // Negative block size indicates the blocks should be concatenated
3026         if (pblocks[i] < 0) {
3027           pblocks[i] = -pblocks[i];
3028           pblocks[nblocks - 1] += pblocks[i];
3029         } else {
3030           pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
3031         }
3032         for (PetscInt j = 1; j < pblocks[i]; j++)
3033           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);
3034       }
3035       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
3036     }
3037     PetscCall(PetscFree(pblocks));
3038   }
3039   PetscCall(MatSetDM(*J, dm));
3040   PetscFunctionReturn(PETSC_SUCCESS);
3041 }
3042 
3043 /*@
3044   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
3045 
3046   Not Collective
3047 
3048   Input Parameter:
3049 . dm - The `DMPLEX`
3050 
3051   Output Parameter:
3052 . subsection - The subdomain section
3053 
3054   Level: developer
3055 
3056 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
3057 @*/
3058 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
3059 {
3060   DM_Plex *mesh = (DM_Plex *)dm->data;
3061 
3062   PetscFunctionBegin;
3063   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3064   if (!mesh->subdomainSection) {
3065     PetscSection section;
3066     PetscSF      sf;
3067 
3068     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
3069     PetscCall(DMGetLocalSection(dm, &section));
3070     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
3071     PetscCall(PetscSFDestroy(&sf));
3072   }
3073   *subsection = mesh->subdomainSection;
3074   PetscFunctionReturn(PETSC_SUCCESS);
3075 }
3076 
3077 /*@
3078   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
3079 
3080   Not Collective
3081 
3082   Input Parameter:
3083 . dm - The `DMPLEX`
3084 
3085   Output Parameters:
3086 + pStart - The first mesh point
3087 - pEnd   - The upper bound for mesh points
3088 
3089   Level: beginner
3090 
3091 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
3092 @*/
3093 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
3094 {
3095   DM_Plex *mesh = (DM_Plex *)dm->data;
3096 
3097   PetscFunctionBegin;
3098   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3099   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
3100   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
3101   PetscFunctionReturn(PETSC_SUCCESS);
3102 }
3103 
3104 /*@
3105   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
3106 
3107   Not Collective
3108 
3109   Input Parameters:
3110 + dm     - The `DMPLEX`
3111 . pStart - The first mesh point
3112 - pEnd   - The upper bound for mesh points
3113 
3114   Level: beginner
3115 
3116 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
3117 @*/
3118 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
3119 {
3120   DM_Plex *mesh = (DM_Plex *)dm->data;
3121 
3122   PetscFunctionBegin;
3123   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3124   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
3125   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
3126   PetscCall(PetscFree(mesh->cellTypes));
3127   PetscFunctionReturn(PETSC_SUCCESS);
3128 }
3129 
3130 /*@
3131   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
3132 
3133   Not Collective
3134 
3135   Input Parameters:
3136 + dm - The `DMPLEX`
3137 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3138 
3139   Output Parameter:
3140 . size - The cone size for point `p`
3141 
3142   Level: beginner
3143 
3144 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3145 @*/
3146 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
3147 {
3148   DM_Plex *mesh = (DM_Plex *)dm->data;
3149 
3150   PetscFunctionBegin;
3151   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3152   PetscAssertPointer(size, 3);
3153   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
3154   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
3155   PetscFunctionReturn(PETSC_SUCCESS);
3156 }
3157 
3158 /*@
3159   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
3160 
3161   Not Collective
3162 
3163   Input Parameters:
3164 + dm   - The `DMPLEX`
3165 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3166 - size - The cone size for point `p`
3167 
3168   Level: beginner
3169 
3170   Note:
3171   This should be called after `DMPlexSetChart()`.
3172 
3173 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
3174 @*/
3175 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
3176 {
3177   DM_Plex *mesh = (DM_Plex *)dm->data;
3178 
3179   PetscFunctionBegin;
3180   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3181   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
3182   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
3183   PetscFunctionReturn(PETSC_SUCCESS);
3184 }
3185 
3186 /*@C
3187   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
3188 
3189   Not Collective
3190 
3191   Input Parameters:
3192 + dm - The `DMPLEX`
3193 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3194 
3195   Output Parameter:
3196 . cone - An array of points which are on the in-edges for point `p`, the length of `cone` is the result of `DMPlexGetConeSize()`
3197 
3198   Level: beginner
3199 
3200   Fortran Notes:
3201   `cone` must be declared with
3202 .vb
3203   PetscInt, pointer :: cone(:)
3204 .ve
3205 
3206   You must call `DMPlexRestoreCone()` after you finish using the array.
3207   `DMPlexRestoreCone()` is not needed/available in C.
3208 
3209 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
3210 @*/
3211 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
3212 {
3213   DM_Plex *mesh = (DM_Plex *)dm->data;
3214   PetscInt off;
3215 
3216   PetscFunctionBegin;
3217   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3218   PetscAssertPointer(cone, 3);
3219   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3220   *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3221   PetscFunctionReturn(PETSC_SUCCESS);
3222 }
3223 
3224 /*@
3225   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
3226 
3227   Not Collective
3228 
3229   Input Parameters:
3230 + dm - The `DMPLEX`
3231 - p  - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3232 
3233   Output Parameters:
3234 + pConesSection - `PetscSection` describing the layout of `pCones`
3235 - pCones        - An `IS` containing the points which are on the in-edges for the point set `p`
3236 
3237   Level: intermediate
3238 
3239 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
3240 @*/
3241 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PeOp PetscSection *pConesSection, PeOp IS *pCones)
3242 {
3243   PetscSection cs, newcs;
3244   PetscInt    *cones;
3245   PetscInt    *newarr = NULL;
3246   PetscInt     n;
3247 
3248   PetscFunctionBegin;
3249   PetscCall(DMPlexGetCones(dm, &cones));
3250   PetscCall(DMPlexGetConeSection(dm, &cs));
3251   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
3252   if (pConesSection) *pConesSection = newcs;
3253   if (pCones) {
3254     PetscCall(PetscSectionGetStorageSize(newcs, &n));
3255     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
3256   }
3257   PetscFunctionReturn(PETSC_SUCCESS);
3258 }
3259 
3260 /*@
3261   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
3262 
3263   Not Collective
3264 
3265   Input Parameters:
3266 + dm     - The `DMPLEX`
3267 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3268 
3269   Output Parameter:
3270 . expandedPoints - An `IS` containing the of vertices recursively expanded from input points
3271 
3272   Level: advanced
3273 
3274   Notes:
3275   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
3276 
3277   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
3278 
3279 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
3280           `DMPlexGetDepth()`, `IS`
3281 @*/
3282 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
3283 {
3284   IS      *expandedPointsAll;
3285   PetscInt depth;
3286 
3287   PetscFunctionBegin;
3288   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3289   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3290   PetscAssertPointer(expandedPoints, 3);
3291   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3292   *expandedPoints = expandedPointsAll[0];
3293   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
3294   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3295   PetscFunctionReturn(PETSC_SUCCESS);
3296 }
3297 
3298 /*@
3299   DMPlexGetConeRecursive - Expand each given point into its cone points and do that recursively until we end up just with vertices
3300   (DAG points of depth 0, i.e., without cones).
3301 
3302   Not Collective
3303 
3304   Input Parameters:
3305 + dm     - The `DMPLEX`
3306 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3307 
3308   Output Parameters:
3309 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3310 . expandedPoints - (optional) An array of index sets with recursively expanded cones
3311 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3312 
3313   Level: advanced
3314 
3315   Notes:
3316   Like `DMPlexGetConeTuple()` but recursive.
3317 
3318   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.
3319   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
3320 
3321   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\:
3322   (1) DAG points in `expandedPoints`[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
3323   (2) DAG points in `expandedPoints`[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
3324 
3325 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3326           `DMPlexGetDepth()`, `PetscSection`, `IS`
3327 @*/
3328 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PeOp PetscInt *depth, PeOp IS *expandedPoints[], PeOp PetscSection *sections[])
3329 {
3330   const PetscInt *arr0 = NULL, *cone = NULL;
3331   PetscInt       *arr = NULL, *newarr = NULL;
3332   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
3333   IS             *expandedPoints_;
3334   PetscSection   *sections_;
3335 
3336   PetscFunctionBegin;
3337   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3338   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3339   if (depth) PetscAssertPointer(depth, 3);
3340   if (expandedPoints) PetscAssertPointer(expandedPoints, 4);
3341   if (sections) PetscAssertPointer(sections, 5);
3342   PetscCall(ISGetLocalSize(points, &n));
3343   PetscCall(ISGetIndices(points, &arr0));
3344   PetscCall(DMPlexGetDepth(dm, &depth_));
3345   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
3346   PetscCall(PetscCalloc1(depth_, &sections_));
3347   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
3348   for (d = depth_ - 1; d >= 0; d--) {
3349     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
3350     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
3351     for (i = 0; i < n; i++) {
3352       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
3353       if (arr[i] >= start && arr[i] < end) {
3354         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
3355         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
3356       } else {
3357         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
3358       }
3359     }
3360     PetscCall(PetscSectionSetUp(sections_[d]));
3361     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
3362     PetscCall(PetscMalloc1(newn, &newarr));
3363     for (i = 0; i < n; i++) {
3364       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
3365       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
3366       if (cn > 1) {
3367         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
3368         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3369       } else {
3370         newarr[co] = arr[i];
3371       }
3372     }
3373     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3374     arr = newarr;
3375     n   = newn;
3376   }
3377   PetscCall(ISRestoreIndices(points, &arr0));
3378   *depth = depth_;
3379   if (expandedPoints) *expandedPoints = expandedPoints_;
3380   else {
3381     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3382     PetscCall(PetscFree(expandedPoints_));
3383   }
3384   if (sections) *sections = sections_;
3385   else {
3386     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3387     PetscCall(PetscFree(sections_));
3388   }
3389   PetscFunctionReturn(PETSC_SUCCESS);
3390 }
3391 
3392 /*@
3393   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3394 
3395   Not Collective
3396 
3397   Input Parameters:
3398 + dm     - The `DMPLEX`
3399 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3400 
3401   Output Parameters:
3402 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3403 . expandedPoints - (optional) An array of recursively expanded cones
3404 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3405 
3406   Level: advanced
3407 
3408   Note:
3409   See `DMPlexGetConeRecursive()`
3410 
3411 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3412           `DMPlexGetDepth()`, `IS`, `PetscSection`
3413 @*/
3414 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PeOp PetscInt *depth, PeOp IS *expandedPoints[], PeOp PetscSection *sections[])
3415 {
3416   PetscInt d, depth_;
3417 
3418   PetscFunctionBegin;
3419   PetscCall(DMPlexGetDepth(dm, &depth_));
3420   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3421   if (depth) *depth = 0;
3422   if (expandedPoints) {
3423     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&(*expandedPoints)[d]));
3424     PetscCall(PetscFree(*expandedPoints));
3425   }
3426   if (sections) {
3427     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&(*sections)[d]));
3428     PetscCall(PetscFree(*sections));
3429   }
3430   PetscFunctionReturn(PETSC_SUCCESS);
3431 }
3432 
3433 /*@
3434   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
3435 
3436   Not Collective
3437 
3438   Input Parameters:
3439 + dm   - The `DMPLEX`
3440 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3441 - cone - An array of points which are on the in-edges for point `p`, its length must have been previously provided with `DMPlexSetConeSize()`
3442 
3443   Level: beginner
3444 
3445   Note:
3446   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3447 
3448 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3449 @*/
3450 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3451 {
3452   DM_Plex *mesh = (DM_Plex *)dm->data;
3453   PetscInt dof, off, c;
3454 
3455   PetscFunctionBegin;
3456   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3457   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3458   if (dof) PetscAssertPointer(cone, 3);
3459   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3460   if (PetscDefined(USE_DEBUG)) {
3461     PetscInt pStart, pEnd;
3462     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3463     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);
3464     for (c = 0; c < dof; ++c) {
3465       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);
3466       mesh->cones[off + c] = cone[c];
3467     }
3468   } else {
3469     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3470   }
3471   PetscFunctionReturn(PETSC_SUCCESS);
3472 }
3473 
3474 /*@C
3475   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3476 
3477   Not Collective
3478 
3479   Input Parameters:
3480 + dm - The `DMPLEX`
3481 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3482 
3483   Output Parameter:
3484 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3485                     integer giving the prescription for cone traversal. Its length is given by the result of `DMPlexSetConeSize()`
3486 
3487   Level: beginner
3488 
3489   Note:
3490   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3491   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3492   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3493   with the identity.
3494 
3495   Fortran Notes:
3496   You must call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3497   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3498 
3499 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetConeSize()`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`,
3500           `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3501 @*/
3502 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3503 {
3504   DM_Plex *mesh = (DM_Plex *)dm->data;
3505   PetscInt off;
3506 
3507   PetscFunctionBegin;
3508   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3509   if (PetscDefined(USE_DEBUG)) {
3510     PetscInt dof;
3511     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3512     if (dof) PetscAssertPointer(coneOrientation, 3);
3513   }
3514   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3515 
3516   *coneOrientation = &mesh->coneOrientations[off];
3517   PetscFunctionReturn(PETSC_SUCCESS);
3518 }
3519 
3520 /*@
3521   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3522 
3523   Not Collective
3524 
3525   Input Parameters:
3526 + dm              - The `DMPLEX`
3527 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3528 - coneOrientation - An array of orientations. Its length is given by the result of `DMPlexSetConeSize()`
3529 
3530   Level: beginner
3531 
3532   Notes:
3533   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3534 
3535   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3536 
3537 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3538 @*/
3539 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3540 {
3541   DM_Plex *mesh = (DM_Plex *)dm->data;
3542   PetscInt pStart, pEnd;
3543   PetscInt dof, off, c;
3544 
3545   PetscFunctionBegin;
3546   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3547   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3548   if (dof) PetscAssertPointer(coneOrientation, 3);
3549   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3550   if (PetscDefined(USE_DEBUG)) {
3551     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3552     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);
3553     for (c = 0; c < dof; ++c) {
3554       PetscInt cdof, o = coneOrientation[c];
3555 
3556       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3557       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);
3558       mesh->coneOrientations[off + c] = o;
3559     }
3560   } else {
3561     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3562   }
3563   PetscFunctionReturn(PETSC_SUCCESS);
3564 }
3565 
3566 /*@
3567   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3568 
3569   Not Collective
3570 
3571   Input Parameters:
3572 + dm        - The `DMPLEX`
3573 . p         - The point, which must lie in the chart set with `DMPlexSetChart()`
3574 . conePos   - The local index in the cone where the point should be put
3575 - conePoint - The mesh point to insert
3576 
3577   Level: beginner
3578 
3579 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3580 @*/
3581 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3582 {
3583   DM_Plex *mesh = (DM_Plex *)dm->data;
3584   PetscInt pStart, pEnd;
3585   PetscInt dof, off;
3586 
3587   PetscFunctionBegin;
3588   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3589   if (PetscDefined(USE_DEBUG)) {
3590     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3591     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);
3592     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);
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->cones[off + conePos] = conePoint;
3598   PetscFunctionReturn(PETSC_SUCCESS);
3599 }
3600 
3601 /*@
3602   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p 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 . conePos         - The local index in the cone where the point should be put
3610 - coneOrientation - The point orientation to insert
3611 
3612   Level: beginner
3613 
3614   Note:
3615   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3616 
3617 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3618 @*/
3619 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3620 {
3621   DM_Plex *mesh = (DM_Plex *)dm->data;
3622   PetscInt pStart, pEnd;
3623   PetscInt dof, off;
3624 
3625   PetscFunctionBegin;
3626   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3627   if (PetscDefined(USE_DEBUG)) {
3628     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3629     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);
3630     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3631     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);
3632   }
3633   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3634   mesh->coneOrientations[off + conePos] = coneOrientation;
3635   PetscFunctionReturn(PETSC_SUCCESS);
3636 }
3637 
3638 /*@C
3639   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3640 
3641   Not collective
3642 
3643   Input Parameters:
3644 + dm - The DMPlex
3645 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3646 
3647   Output Parameters:
3648 + cone - An array of points which are on the in-edges for point `p`
3649 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3650          integer giving the prescription for cone traversal.
3651 
3652   Level: beginner
3653 
3654   Notes:
3655   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3656   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3657   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3658   with the identity.
3659 
3660   You must also call `DMPlexRestoreOrientedCone()` after you finish using the returned array.
3661 
3662   Fortran Notes:
3663   `cone` and `ornt` must be declared with
3664 .vb
3665   PetscInt, pointer :: cone(:)
3666   PetscInt, pointer :: ornt(:)
3667 .ve
3668 
3669 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3670 @*/
3671 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, PeOp const PetscInt *cone[], PeOp const PetscInt *ornt[])
3672 {
3673   DM_Plex *mesh = (DM_Plex *)dm->data;
3674 
3675   PetscFunctionBegin;
3676   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3677   if (mesh->tr) {
3678     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3679   } else {
3680     PetscInt off;
3681     if (PetscDefined(USE_DEBUG)) {
3682       PetscInt dof;
3683       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3684       if (dof) {
3685         if (cone) PetscAssertPointer(cone, 3);
3686         if (ornt) PetscAssertPointer(ornt, 4);
3687       }
3688     }
3689     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3690     if (cone) *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3691     if (ornt) *ornt = PetscSafePointerPlusOffset(mesh->coneOrientations, off);
3692   }
3693   PetscFunctionReturn(PETSC_SUCCESS);
3694 }
3695 
3696 /*@C
3697   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG obtained with `DMPlexGetOrientedCone()`
3698 
3699   Not Collective
3700 
3701   Input Parameters:
3702 + dm   - The DMPlex
3703 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3704 . cone - An array of points which are on the in-edges for point p
3705 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3706          integer giving the prescription for cone traversal.
3707 
3708   Level: beginner
3709 
3710 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3711 @*/
3712 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3713 {
3714   DM_Plex *mesh = (DM_Plex *)dm->data;
3715 
3716   PetscFunctionBegin;
3717   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3718   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3719   PetscFunctionReturn(PETSC_SUCCESS);
3720 }
3721 
3722 /*@
3723   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3724 
3725   Not Collective
3726 
3727   Input Parameters:
3728 + dm - The `DMPLEX`
3729 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3730 
3731   Output Parameter:
3732 . size - The support size for point `p`
3733 
3734   Level: beginner
3735 
3736 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3737 @*/
3738 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3739 {
3740   DM_Plex *mesh = (DM_Plex *)dm->data;
3741 
3742   PetscFunctionBegin;
3743   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3744   PetscAssertPointer(size, 3);
3745   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3746   PetscFunctionReturn(PETSC_SUCCESS);
3747 }
3748 
3749 /*@
3750   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3751 
3752   Not Collective
3753 
3754   Input Parameters:
3755 + dm   - The `DMPLEX`
3756 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3757 - size - The support size for point `p`
3758 
3759   Level: beginner
3760 
3761   Note:
3762   This should be called after `DMPlexSetChart()`.
3763 
3764 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3765 @*/
3766 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3767 {
3768   DM_Plex *mesh = (DM_Plex *)dm->data;
3769 
3770   PetscFunctionBegin;
3771   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3772   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3773   PetscFunctionReturn(PETSC_SUCCESS);
3774 }
3775 
3776 /*@C
3777   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3778 
3779   Not Collective
3780 
3781   Input Parameters:
3782 + dm - The `DMPLEX`
3783 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3784 
3785   Output Parameter:
3786 . support - An array of points which are on the out-edges for point `p`, its length is that obtained from `DMPlexGetSupportSize()`
3787 
3788   Level: beginner
3789 
3790   Fortran Notes:
3791   `support` must be declared with
3792 .vb
3793   PetscInt, pointer :: support(:)
3794 .ve
3795 
3796   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3797   `DMPlexRestoreSupport()` is not needed/available in C.
3798 
3799 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3800 @*/
3801 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3802 {
3803   DM_Plex *mesh = (DM_Plex *)dm->data;
3804   PetscInt off;
3805 
3806   PetscFunctionBegin;
3807   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3808   PetscAssertPointer(support, 3);
3809   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3810   *support = PetscSafePointerPlusOffset(mesh->supports, off);
3811   PetscFunctionReturn(PETSC_SUCCESS);
3812 }
3813 
3814 /*@
3815   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
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 - support - An array of points which are on the out-edges for point `p`, its length is that obtained from `DMPlexGetSupportSize()`
3823 
3824   Level: beginner
3825 
3826   Note:
3827   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3828 
3829 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3830 @*/
3831 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3832 {
3833   DM_Plex *mesh = (DM_Plex *)dm->data;
3834   PetscInt pStart, pEnd;
3835   PetscInt dof, off, c;
3836 
3837   PetscFunctionBegin;
3838   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3839   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3840   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3841   if (dof) PetscAssertPointer(support, 3);
3842   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3843   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);
3844   for (c = 0; c < dof; ++c) {
3845     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);
3846     mesh->supports[off + c] = support[c];
3847   }
3848   PetscFunctionReturn(PETSC_SUCCESS);
3849 }
3850 
3851 /*@
3852   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3853 
3854   Not Collective
3855 
3856   Input Parameters:
3857 + dm           - The `DMPLEX`
3858 . p            - The point, which must lie in the chart set with `DMPlexSetChart()`
3859 . supportPos   - The local index in the cone where the point should be put
3860 - supportPoint - The mesh point to insert
3861 
3862   Level: beginner
3863 
3864 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3865 @*/
3866 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3867 {
3868   DM_Plex *mesh = (DM_Plex *)dm->data;
3869   PetscInt pStart, pEnd;
3870   PetscInt dof, off;
3871 
3872   PetscFunctionBegin;
3873   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3874   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3875   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3876   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3877   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);
3878   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);
3879   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);
3880   mesh->supports[off + supportPos] = supportPoint;
3881   PetscFunctionReturn(PETSC_SUCCESS);
3882 }
3883 
3884 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3885 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3886 {
3887   switch (ct) {
3888   case DM_POLYTOPE_SEGMENT:
3889     if (o == -1) return -2;
3890     break;
3891   case DM_POLYTOPE_TRIANGLE:
3892     if (o == -3) return -1;
3893     if (o == -2) return -3;
3894     if (o == -1) return -2;
3895     break;
3896   case DM_POLYTOPE_QUADRILATERAL:
3897     if (o == -4) return -2;
3898     if (o == -3) return -1;
3899     if (o == -2) return -4;
3900     if (o == -1) return -3;
3901     break;
3902   default:
3903     return o;
3904   }
3905   return o;
3906 }
3907 
3908 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3909 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3910 {
3911   switch (ct) {
3912   case DM_POLYTOPE_SEGMENT:
3913     if ((o == -2) || (o == 1)) return -1;
3914     if (o == -1) return 0;
3915     break;
3916   case DM_POLYTOPE_TRIANGLE:
3917     if (o == -3) return -2;
3918     if (o == -2) return -1;
3919     if (o == -1) return -3;
3920     break;
3921   case DM_POLYTOPE_QUADRILATERAL:
3922     if (o == -4) return -2;
3923     if (o == -3) return -1;
3924     if (o == -2) return -4;
3925     if (o == -1) return -3;
3926     break;
3927   default:
3928     return o;
3929   }
3930   return o;
3931 }
3932 
3933 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3934 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3935 {
3936   PetscInt pStart, pEnd, p;
3937 
3938   PetscFunctionBegin;
3939   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3940   for (p = pStart; p < pEnd; ++p) {
3941     const PetscInt *cone, *ornt;
3942     PetscInt        coneSize, c;
3943 
3944     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3945     PetscCall(DMPlexGetCone(dm, p, &cone));
3946     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3947     for (c = 0; c < coneSize; ++c) {
3948       DMPolytopeType ct;
3949       const PetscInt o = ornt[c];
3950 
3951       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3952       switch (ct) {
3953       case DM_POLYTOPE_SEGMENT:
3954         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3955         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3956         break;
3957       case DM_POLYTOPE_TRIANGLE:
3958         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3959         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3960         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3961         break;
3962       case DM_POLYTOPE_QUADRILATERAL:
3963         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3964         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3965         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3966         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3967         break;
3968       default:
3969         break;
3970       }
3971     }
3972   }
3973   PetscFunctionReturn(PETSC_SUCCESS);
3974 }
3975 
3976 static inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3977 {
3978   DM_Plex *mesh = (DM_Plex *)dm->data;
3979 
3980   PetscFunctionBeginHot;
3981   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3982     if (useCone) {
3983       PetscCall(DMPlexGetConeSize(dm, p, size));
3984       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3985     } else {
3986       PetscCall(DMPlexGetSupportSize(dm, p, size));
3987       PetscCall(DMPlexGetSupport(dm, p, arr));
3988     }
3989   } else {
3990     if (useCone) {
3991       const PetscSection s   = mesh->coneSection;
3992       const PetscInt     ps  = p - s->pStart;
3993       const PetscInt     off = s->atlasOff[ps];
3994 
3995       *size = s->atlasDof[ps];
3996       *arr  = mesh->cones + off;
3997       *ornt = mesh->coneOrientations + off;
3998     } else {
3999       const PetscSection s   = mesh->supportSection;
4000       const PetscInt     ps  = p - s->pStart;
4001       const PetscInt     off = s->atlasOff[ps];
4002 
4003       *size = s->atlasDof[ps];
4004       *arr  = mesh->supports + off;
4005     }
4006   }
4007   PetscFunctionReturn(PETSC_SUCCESS);
4008 }
4009 
4010 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
4011 {
4012   DM_Plex *mesh = (DM_Plex *)dm->data;
4013 
4014   PetscFunctionBeginHot;
4015   if (PetscDefined(USE_DEBUG) || mesh->tr) {
4016     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
4017   }
4018   PetscFunctionReturn(PETSC_SUCCESS);
4019 }
4020 
4021 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4022 {
4023   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
4024   PetscInt       *closure;
4025   const PetscInt *tmp = NULL, *tmpO = NULL;
4026   PetscInt        off = 0, tmpSize, t;
4027 
4028   PetscFunctionBeginHot;
4029   if (ornt) {
4030     PetscCall(DMPlexGetCellType(dm, p, &ct));
4031     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;
4032   }
4033   if (*points) {
4034     closure = *points;
4035   } else {
4036     PetscInt maxConeSize, maxSupportSize;
4037     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
4038     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
4039   }
4040   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
4041   if (ct == DM_POLYTOPE_UNKNOWN) {
4042     closure[off++] = p;
4043     closure[off++] = 0;
4044     for (t = 0; t < tmpSize; ++t) {
4045       closure[off++] = tmp[t];
4046       closure[off++] = tmpO ? tmpO[t] : 0;
4047     }
4048   } else {
4049     const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, ornt);
4050 
4051     /* We assume that cells with a valid type have faces with a valid type */
4052     closure[off++] = p;
4053     closure[off++] = ornt;
4054     for (t = 0; t < tmpSize; ++t) {
4055       DMPolytopeType ft;
4056 
4057       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
4058       closure[off++] = tmp[arr[t]];
4059       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
4060     }
4061   }
4062   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
4063   if (numPoints) *numPoints = tmpSize + 1;
4064   if (points) *points = closure;
4065   PetscFunctionReturn(PETSC_SUCCESS);
4066 }
4067 
4068 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
4069 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
4070 {
4071   const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, o);
4072   const PetscInt *cone, *ornt;
4073   PetscInt       *pts, *closure = NULL;
4074   DMPolytopeType  ft;
4075   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
4076   PetscInt        dim, coneSize, c, d, clSize, cl;
4077 
4078   PetscFunctionBeginHot;
4079   PetscCall(DMGetDimension(dm, &dim));
4080   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
4081   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
4082   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
4083   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
4084   maxSize       = PetscMax(coneSeries, supportSeries);
4085   if (*points) {
4086     pts = *points;
4087   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
4088   c        = 0;
4089   pts[c++] = point;
4090   pts[c++] = o;
4091   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
4092   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
4093   for (cl = 0; cl < clSize * 2; cl += 2) {
4094     pts[c++] = closure[cl];
4095     pts[c++] = closure[cl + 1];
4096   }
4097   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
4098   for (cl = 0; cl < clSize * 2; cl += 2) {
4099     pts[c++] = closure[cl];
4100     pts[c++] = closure[cl + 1];
4101   }
4102   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
4103   for (d = 2; d < coneSize; ++d) {
4104     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
4105     pts[c++] = cone[arr[d * 2 + 0]];
4106     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
4107   }
4108   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
4109   if (dim >= 3) {
4110     for (d = 2; d < coneSize; ++d) {
4111       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
4112       const PetscInt *fcone, *fornt;
4113       PetscInt        fconeSize, fc, i;
4114 
4115       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
4116       const PetscInt *farr = DMPolytopeTypeGetArrangement(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
4117       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
4118       for (fc = 0; fc < fconeSize; ++fc) {
4119         const PetscInt cp = fcone[farr[fc * 2 + 0]];
4120         const PetscInt co = farr[fc * 2 + 1];
4121 
4122         for (i = 0; i < c; i += 2)
4123           if (pts[i] == cp) break;
4124         if (i == c) {
4125           PetscCall(DMPlexGetCellType(dm, cp, &ft));
4126           pts[c++] = cp;
4127           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
4128         }
4129       }
4130       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
4131     }
4132   }
4133   *numPoints = c / 2;
4134   *points    = pts;
4135   PetscFunctionReturn(PETSC_SUCCESS);
4136 }
4137 
4138 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4139 {
4140   DMPolytopeType ct;
4141   PetscInt      *closure, *fifo;
4142   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
4143   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
4144   PetscInt       depth, maxSize;
4145 
4146   PetscFunctionBeginHot;
4147   PetscCall(DMPlexGetDepth(dm, &depth));
4148   if (depth == 1) {
4149     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
4150     PetscFunctionReturn(PETSC_SUCCESS);
4151   }
4152   PetscCall(DMPlexGetCellType(dm, p, &ct));
4153   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;
4154   if (DMPolytopeTypeIsHybrid(ct) && ct != DM_POLYTOPE_POINT_PRISM_TENSOR) {
4155     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
4156     PetscFunctionReturn(PETSC_SUCCESS);
4157   }
4158   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
4159   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
4160   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
4161   maxSize       = PetscMax(coneSeries, supportSeries);
4162   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4163   if (*points) {
4164     closure = *points;
4165   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
4166   closure[closureSize++] = p;
4167   closure[closureSize++] = ornt;
4168   fifo[fifoSize++]       = p;
4169   fifo[fifoSize++]       = ornt;
4170   fifo[fifoSize++]       = ct;
4171   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
4172   while (fifoSize - fifoStart) {
4173     const PetscInt       q    = fifo[fifoStart++];
4174     const PetscInt       o    = fifo[fifoStart++];
4175     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
4176     const PetscInt      *qarr = DMPolytopeTypeGetArrangement(qt, o);
4177     const PetscInt      *tmp, *tmpO = NULL;
4178     PetscInt             tmpSize, t;
4179 
4180     if (PetscDefined(USE_DEBUG)) {
4181       PetscInt nO = DMPolytopeTypeGetNumArrangements(qt) / 2;
4182       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);
4183     }
4184     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4185     for (t = 0; t < tmpSize; ++t) {
4186       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
4187       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
4188       const PetscInt cp = tmp[ip];
4189       PetscCall(DMPlexGetCellType(dm, cp, &ct));
4190       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
4191       PetscInt       c;
4192 
4193       /* Check for duplicate */
4194       for (c = 0; c < closureSize; c += 2) {
4195         if (closure[c] == cp) break;
4196       }
4197       if (c == closureSize) {
4198         closure[closureSize++] = cp;
4199         closure[closureSize++] = co;
4200         fifo[fifoSize++]       = cp;
4201         fifo[fifoSize++]       = co;
4202         fifo[fifoSize++]       = ct;
4203       }
4204     }
4205     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4206   }
4207   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4208   if (numPoints) *numPoints = closureSize / 2;
4209   if (points) *points = closure;
4210   PetscFunctionReturn(PETSC_SUCCESS);
4211 }
4212 
4213 /*@C
4214   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
4215 
4216   Not Collective
4217 
4218   Input Parameters:
4219 + dm      - The `DMPLEX`
4220 . p       - The mesh point
4221 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
4222 
4223   Input/Output Parameter:
4224 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
4225            if *points is `NULL` on input, internal storage will be returned, use `DMPlexRestoreTransitiveClosure()`,
4226            otherwise the provided array is used to hold the values
4227 
4228   Output Parameter:
4229 . numPoints - The number of points in the closure, so `points` is of size 2*`numPoints`
4230 
4231   Level: beginner
4232 
4233   Note:
4234   If using internal storage (points is `NULL` on input), each call overwrites the last output.
4235 
4236   Fortran Notes:
4237   `points` must be declared with
4238 .vb
4239   PetscInt, pointer :: points(:)
4240 .ve
4241   and is always allocated by the function.
4242 
4243   Pass `PETSC_NULL_INTEGER` for `numPoints` if it is not needed
4244 
4245 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4246 @*/
4247 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4248 {
4249   PetscFunctionBeginHot;
4250   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4251   if (numPoints) PetscAssertPointer(numPoints, 4);
4252   if (points) PetscAssertPointer(points, 5);
4253   if (PetscDefined(USE_DEBUG)) {
4254     PetscInt pStart, pEnd;
4255     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4256     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);
4257   }
4258   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
4259   PetscFunctionReturn(PETSC_SUCCESS);
4260 }
4261 
4262 /*@C
4263   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
4264 
4265   Not Collective
4266 
4267   Input Parameters:
4268 + dm        - The `DMPLEX`
4269 . p         - The mesh point
4270 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
4271 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4272 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
4273 
4274   Level: beginner
4275 
4276   Note:
4277   If not using internal storage (points is not `NULL` on input), this call is unnecessary
4278 
4279 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4280 @*/
4281 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4282 {
4283   PetscFunctionBeginHot;
4284   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4285   if (numPoints) *numPoints = 0;
4286   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
4287   PetscFunctionReturn(PETSC_SUCCESS);
4288 }
4289 
4290 /*@
4291   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
4292 
4293   Not Collective
4294 
4295   Input Parameter:
4296 . dm - The `DMPLEX`
4297 
4298   Output Parameters:
4299 + maxConeSize    - The maximum number of in-edges
4300 - maxSupportSize - The maximum number of out-edges
4301 
4302   Level: beginner
4303 
4304 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
4305 @*/
4306 PetscErrorCode DMPlexGetMaxSizes(DM dm, PeOp PetscInt *maxConeSize, PeOp PetscInt *maxSupportSize)
4307 {
4308   DM_Plex *mesh = (DM_Plex *)dm->data;
4309 
4310   PetscFunctionBegin;
4311   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4312   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
4313   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
4314   PetscFunctionReturn(PETSC_SUCCESS);
4315 }
4316 
4317 PetscErrorCode DMSetUp_Plex(DM dm)
4318 {
4319   DM_Plex *mesh = (DM_Plex *)dm->data;
4320   PetscInt size, maxSupportSize;
4321 
4322   PetscFunctionBegin;
4323   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4324   PetscCall(PetscSectionSetUp(mesh->coneSection));
4325   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
4326   PetscCall(PetscMalloc1(size, &mesh->cones));
4327   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
4328   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4329   if (maxSupportSize) {
4330     PetscCall(PetscSectionSetUp(mesh->supportSection));
4331     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
4332     PetscCall(PetscMalloc1(size, &mesh->supports));
4333   }
4334   PetscFunctionReturn(PETSC_SUCCESS);
4335 }
4336 
4337 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
4338 {
4339   PetscFunctionBegin;
4340   if (subdm) PetscCall(DMClone(dm, subdm));
4341   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, NULL, NULL, is, subdm));
4342   if (subdm) (*subdm)->useNatural = dm->useNatural;
4343   if (dm->useNatural && dm->sfMigration) {
4344     PetscSF sfNatural;
4345 
4346     (*subdm)->sfMigration = dm->sfMigration;
4347     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
4348     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
4349     (*subdm)->sfNatural = sfNatural;
4350   }
4351   PetscFunctionReturn(PETSC_SUCCESS);
4352 }
4353 
4354 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
4355 {
4356   PetscInt i = 0;
4357 
4358   PetscFunctionBegin;
4359   PetscCall(DMClone(dms[0], superdm));
4360   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
4361   (*superdm)->useNatural = PETSC_FALSE;
4362   for (i = 0; i < len; i++) {
4363     if (dms[i]->useNatural && dms[i]->sfMigration) {
4364       PetscSF sfNatural;
4365 
4366       (*superdm)->sfMigration = dms[i]->sfMigration;
4367       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
4368       (*superdm)->useNatural = PETSC_TRUE;
4369       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
4370       (*superdm)->sfNatural = sfNatural;
4371       break;
4372     }
4373   }
4374   PetscFunctionReturn(PETSC_SUCCESS);
4375 }
4376 
4377 /*@
4378   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
4379 
4380   Not Collective
4381 
4382   Input Parameter:
4383 . dm - The `DMPLEX`
4384 
4385   Level: beginner
4386 
4387   Note:
4388   This should be called after all calls to `DMPlexSetCone()`
4389 
4390 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4391 @*/
4392 PetscErrorCode DMPlexSymmetrize(DM dm)
4393 {
4394   DM_Plex  *mesh = (DM_Plex *)dm->data;
4395   PetscInt *offsets;
4396   PetscInt  supportSize;
4397   PetscInt  pStart, pEnd, p;
4398 
4399   PetscFunctionBegin;
4400   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4401   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4402   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4403   /* Calculate support sizes */
4404   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4405   for (p = pStart; p < pEnd; ++p) {
4406     PetscInt dof, off, c;
4407 
4408     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4409     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4410     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4411   }
4412   PetscCall(PetscSectionSetUp(mesh->supportSection));
4413   /* Calculate supports */
4414   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4415   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4416   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4417   for (p = pStart; p < pEnd; ++p) {
4418     PetscInt dof, off, c;
4419 
4420     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4421     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4422     for (c = off; c < off + dof; ++c) {
4423       const PetscInt q = mesh->cones[c];
4424       PetscInt       offS;
4425 
4426       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4427 
4428       mesh->supports[offS + offsets[q]] = p;
4429       ++offsets[q];
4430     }
4431   }
4432   PetscCall(PetscFree(offsets));
4433   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4434   PetscFunctionReturn(PETSC_SUCCESS);
4435 }
4436 
4437 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4438 {
4439   IS stratumIS;
4440 
4441   PetscFunctionBegin;
4442   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4443   if (PetscDefined(USE_DEBUG)) {
4444     PetscInt  qStart, qEnd, numLevels, level;
4445     PetscBool overlap = PETSC_FALSE;
4446     PetscCall(DMLabelGetNumValues(label, &numLevels));
4447     for (level = 0; level < numLevels; level++) {
4448       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4449       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4450         overlap = PETSC_TRUE;
4451         break;
4452       }
4453     }
4454     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);
4455   }
4456   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4457   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4458   PetscCall(ISDestroy(&stratumIS));
4459   PetscFunctionReturn(PETSC_SUCCESS);
4460 }
4461 
4462 static PetscErrorCode DMPlexStratify_CellType_Private(DM dm, DMLabel label)
4463 {
4464   PetscInt *pMin, *pMax;
4465   PetscInt  pStart, pEnd;
4466   PetscInt  dmin = PETSC_INT_MAX, dmax = PETSC_INT_MIN;
4467 
4468   PetscFunctionBegin;
4469   {
4470     DMLabel label2;
4471 
4472     PetscCall(DMPlexGetCellTypeLabel(dm, &label2));
4473     PetscCall(PetscObjectViewFromOptions((PetscObject)label2, NULL, "-ct_view"));
4474   }
4475   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4476   for (PetscInt p = pStart; p < pEnd; ++p) {
4477     DMPolytopeType ct;
4478 
4479     PetscCall(DMPlexGetCellType(dm, p, &ct));
4480     dmin = PetscMin(DMPolytopeTypeGetDim(ct), dmin);
4481     dmax = PetscMax(DMPolytopeTypeGetDim(ct), dmax);
4482   }
4483   PetscCall(PetscMalloc2(dmax + 1, &pMin, dmax + 1, &pMax));
4484   for (PetscInt d = dmin; d <= dmax; ++d) {
4485     pMin[d] = PETSC_INT_MAX;
4486     pMax[d] = PETSC_INT_MIN;
4487   }
4488   for (PetscInt p = pStart; p < pEnd; ++p) {
4489     DMPolytopeType ct;
4490     PetscInt       d;
4491 
4492     PetscCall(DMPlexGetCellType(dm, p, &ct));
4493     d       = DMPolytopeTypeGetDim(ct);
4494     pMin[d] = PetscMin(p, pMin[d]);
4495     pMax[d] = PetscMax(p, pMax[d]);
4496   }
4497   for (PetscInt d = dmin; d <= dmax; ++d) {
4498     if (pMin[d] > pMax[d]) continue;
4499     PetscCall(DMPlexCreateDepthStratum(dm, label, d, pMin[d], pMax[d] + 1));
4500   }
4501   PetscCall(PetscFree2(pMin, pMax));
4502   PetscFunctionReturn(PETSC_SUCCESS);
4503 }
4504 
4505 static PetscErrorCode DMPlexStratify_Topological_Private(DM dm, DMLabel label)
4506 {
4507   PetscInt pStart, pEnd;
4508   PetscInt numRoots = 0, numLeaves = 0;
4509 
4510   PetscFunctionBegin;
4511   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4512   {
4513     /* Initialize roots and count leaves */
4514     PetscInt sMin = PETSC_INT_MAX;
4515     PetscInt sMax = PETSC_INT_MIN;
4516     PetscInt coneSize, supportSize;
4517 
4518     for (PetscInt p = pStart; p < pEnd; ++p) {
4519       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4520       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4521       if (!coneSize && supportSize) {
4522         sMin = PetscMin(p, sMin);
4523         sMax = PetscMax(p, sMax);
4524         ++numRoots;
4525       } else if (!supportSize && coneSize) {
4526         ++numLeaves;
4527       } else if (!supportSize && !coneSize) {
4528         /* Isolated points */
4529         sMin = PetscMin(p, sMin);
4530         sMax = PetscMax(p, sMax);
4531       }
4532     }
4533     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4534   }
4535 
4536   if (numRoots + numLeaves == (pEnd - pStart)) {
4537     PetscInt sMin = PETSC_INT_MAX;
4538     PetscInt sMax = PETSC_INT_MIN;
4539     PetscInt coneSize, supportSize;
4540 
4541     for (PetscInt p = pStart; p < pEnd; ++p) {
4542       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4543       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4544       if (!supportSize && coneSize) {
4545         sMin = PetscMin(p, sMin);
4546         sMax = PetscMax(p, sMax);
4547       }
4548     }
4549     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4550   } else {
4551     PetscInt level = 0;
4552     PetscInt qStart, qEnd;
4553 
4554     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4555     while (qEnd > qStart) {
4556       PetscInt sMin = PETSC_INT_MAX;
4557       PetscInt sMax = PETSC_INT_MIN;
4558 
4559       for (PetscInt q = qStart; q < qEnd; ++q) {
4560         const PetscInt *support;
4561         PetscInt        supportSize;
4562 
4563         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4564         PetscCall(DMPlexGetSupport(dm, q, &support));
4565         for (PetscInt s = 0; s < supportSize; ++s) {
4566           sMin = PetscMin(support[s], sMin);
4567           sMax = PetscMax(support[s], sMax);
4568         }
4569       }
4570       PetscCall(DMLabelGetNumValues(label, &level));
4571       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4572       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4573     }
4574   }
4575   PetscFunctionReturn(PETSC_SUCCESS);
4576 }
4577 
4578 /*@
4579   DMPlexStratify - Computes the strata for all points in the `DMPLEX`
4580 
4581   Collective
4582 
4583   Input Parameter:
4584 . dm - The `DMPLEX`
4585 
4586   Level: beginner
4587 
4588   Notes:
4589   The strata group all points of the same grade, and this function calculates the strata. This
4590   grade can be seen as the height (or depth) of the point in the DAG.
4591 
4592   The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4593   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram).
4594   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4595   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4596   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4597   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4598   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4599 
4600   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4601   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4602   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
4603   to interpolate only that one (e0), so that
4604 .vb
4605   cone(c0) = {e0, v2}
4606   cone(e0) = {v0, v1}
4607 .ve
4608   If `DMPlexStratify()` is run on this mesh, it will give depths
4609 .vb
4610    depth 0 = {v0, v1, v2}
4611    depth 1 = {e0, c0}
4612 .ve
4613   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4614 
4615   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4616 
4617 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4618 @*/
4619 PetscErrorCode DMPlexStratify(DM dm)
4620 {
4621   DM_Plex  *mesh = (DM_Plex *)dm->data;
4622   DMLabel   label;
4623   PetscBool flg = PETSC_FALSE;
4624 
4625   PetscFunctionBegin;
4626   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4627   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4628 
4629   // Create depth label
4630   PetscCall(DMRemoveLabel(dm, "depth", NULL));
4631   PetscCall(DMCreateLabel(dm, "depth"));
4632   PetscCall(DMPlexGetDepthLabel(dm, &label));
4633 
4634   PetscCall(PetscOptionsGetBool(NULL, dm->hdr.prefix, "-dm_plex_stratify_celltype", &flg, NULL));
4635   if (flg) PetscCall(DMPlexStratify_CellType_Private(dm, label));
4636   else PetscCall(DMPlexStratify_Topological_Private(dm, label));
4637 
4638   { /* just in case there is an empty process */
4639     PetscInt numValues, maxValues = 0, v;
4640 
4641     PetscCall(DMLabelGetNumValues(label, &numValues));
4642     PetscCallMPI(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4643     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4644   }
4645   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4646   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4647   PetscFunctionReturn(PETSC_SUCCESS);
4648 }
4649 
4650 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4651 {
4652   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4653   PetscInt       dim, depth, pheight, coneSize;
4654   PetscBool      preferTensor;
4655 
4656   PetscFunctionBeginHot;
4657   PetscCall(DMGetDimension(dm, &dim));
4658   PetscCall(DMPlexGetDepth(dm, &depth));
4659   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4660   PetscCall(DMPlexGetInterpolatePreferTensor(dm, &preferTensor));
4661   pheight = depth - pdepth;
4662   if (depth <= 1) {
4663     switch (pdepth) {
4664     case 0:
4665       ct = DM_POLYTOPE_POINT;
4666       break;
4667     case 1:
4668       switch (coneSize) {
4669       case 2:
4670         ct = DM_POLYTOPE_SEGMENT;
4671         break;
4672       case 3:
4673         ct = DM_POLYTOPE_TRIANGLE;
4674         break;
4675       case 4:
4676         switch (dim) {
4677         case 2:
4678           ct = DM_POLYTOPE_QUADRILATERAL;
4679           break;
4680         case 3:
4681           ct = DM_POLYTOPE_TETRAHEDRON;
4682           break;
4683         default:
4684           break;
4685         }
4686         break;
4687       case 5:
4688         ct = DM_POLYTOPE_PYRAMID;
4689         break;
4690       case 6:
4691         ct = preferTensor ? DM_POLYTOPE_TRI_PRISM_TENSOR : DM_POLYTOPE_TRI_PRISM;
4692         break;
4693       case 8:
4694         ct = DM_POLYTOPE_HEXAHEDRON;
4695         break;
4696       default:
4697         break;
4698       }
4699     }
4700   } else {
4701     if (pdepth == 0) {
4702       ct = DM_POLYTOPE_POINT;
4703     } else if (pheight == 0) {
4704       switch (dim) {
4705       case 1:
4706         switch (coneSize) {
4707         case 2:
4708           ct = DM_POLYTOPE_SEGMENT;
4709           break;
4710         default:
4711           break;
4712         }
4713         break;
4714       case 2:
4715         switch (coneSize) {
4716         case 3:
4717           ct = DM_POLYTOPE_TRIANGLE;
4718           break;
4719         case 4:
4720           ct = DM_POLYTOPE_QUADRILATERAL;
4721           break;
4722         default:
4723           break;
4724         }
4725         break;
4726       case 3:
4727         switch (coneSize) {
4728         case 4:
4729           ct = DM_POLYTOPE_TETRAHEDRON;
4730           break;
4731         case 5: {
4732           const PetscInt *cone;
4733           PetscInt        faceConeSize;
4734 
4735           PetscCall(DMPlexGetCone(dm, p, &cone));
4736           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4737           switch (faceConeSize) {
4738           case 3:
4739             ct = preferTensor ? DM_POLYTOPE_TRI_PRISM_TENSOR : DM_POLYTOPE_TRI_PRISM;
4740             break;
4741           case 4:
4742             ct = DM_POLYTOPE_PYRAMID;
4743             break;
4744           }
4745         } break;
4746         case 6:
4747           ct = DM_POLYTOPE_HEXAHEDRON;
4748           break;
4749         default:
4750           break;
4751         }
4752         break;
4753       default:
4754         break;
4755       }
4756     } else if (pheight > 0) {
4757       switch (coneSize) {
4758       case 2:
4759         ct = DM_POLYTOPE_SEGMENT;
4760         break;
4761       case 3:
4762         ct = DM_POLYTOPE_TRIANGLE;
4763         break;
4764       case 4:
4765         ct = DM_POLYTOPE_QUADRILATERAL;
4766         break;
4767       default:
4768         break;
4769       }
4770     }
4771   }
4772   *pt = ct;
4773   PetscFunctionReturn(PETSC_SUCCESS);
4774 }
4775 
4776 /*@
4777   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4778 
4779   Collective
4780 
4781   Input Parameter:
4782 . dm - The `DMPLEX`
4783 
4784   Level: developer
4785 
4786   Note:
4787   This function is normally called automatically when a cell type is requested. It creates an
4788   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4789   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4790 
4791   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4792 
4793 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4794 @*/
4795 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4796 {
4797   DM_Plex *mesh;
4798   DMLabel  ctLabel;
4799   PetscInt pStart, pEnd, p;
4800 
4801   PetscFunctionBegin;
4802   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4803   mesh = (DM_Plex *)dm->data;
4804   PetscCall(DMCreateLabel(dm, "celltype"));
4805   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4806   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4807   PetscCall(PetscFree(mesh->cellTypes));
4808   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4809   for (p = pStart; p < pEnd; ++p) {
4810     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4811     PetscInt       pdepth;
4812 
4813     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4814     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4815     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]);
4816     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4817     mesh->cellTypes[p - pStart].value_as_uint8 = (uint8_t)ct;
4818   }
4819   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4820   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4821   PetscFunctionReturn(PETSC_SUCCESS);
4822 }
4823 
4824 /*@C
4825   DMPlexGetJoin - Get an array for the join of the set of points
4826 
4827   Not Collective
4828 
4829   Input Parameters:
4830 + dm        - The `DMPLEX` object
4831 . numPoints - The number of input points for the join
4832 - points    - The input points
4833 
4834   Output Parameters:
4835 + numCoveredPoints - The number of points in the join
4836 - coveredPoints    - The points in the join
4837 
4838   Level: intermediate
4839 
4840   Note:
4841   Currently, this is restricted to a single level join
4842 
4843   Fortran Notes:
4844   `converedPoints` must be declared with
4845 .vb
4846   PetscInt, pointer :: coveredPints(:)
4847 .ve
4848 
4849 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4850 @*/
4851 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4852 {
4853   DM_Plex  *mesh = (DM_Plex *)dm->data;
4854   PetscInt *join[2];
4855   PetscInt  joinSize, i = 0;
4856   PetscInt  dof, off, p, c, m;
4857   PetscInt  maxSupportSize;
4858 
4859   PetscFunctionBegin;
4860   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4861   PetscAssertPointer(points, 3);
4862   PetscAssertPointer(numCoveredPoints, 4);
4863   PetscAssertPointer(coveredPoints, 5);
4864   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4865   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4866   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4867   /* Copy in support of first point */
4868   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4869   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4870   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4871   /* Check each successive support */
4872   for (p = 1; p < numPoints; ++p) {
4873     PetscInt newJoinSize = 0;
4874 
4875     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4876     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4877     for (c = 0; c < dof; ++c) {
4878       const PetscInt point = mesh->supports[off + c];
4879 
4880       for (m = 0; m < joinSize; ++m) {
4881         if (point == join[i][m]) {
4882           join[1 - i][newJoinSize++] = point;
4883           break;
4884         }
4885       }
4886     }
4887     joinSize = newJoinSize;
4888     i        = 1 - i;
4889   }
4890   *numCoveredPoints = joinSize;
4891   *coveredPoints    = join[i];
4892   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4893   PetscFunctionReturn(PETSC_SUCCESS);
4894 }
4895 
4896 /*@C
4897   DMPlexRestoreJoin - Restore an array for the join of the set of points obtained with `DMPlexGetJoin()`
4898 
4899   Not Collective
4900 
4901   Input Parameters:
4902 + dm        - The `DMPLEX` object
4903 . numPoints - The number of input points for the join
4904 - points    - The input points
4905 
4906   Output Parameters:
4907 + numCoveredPoints - The number of points in the join
4908 - coveredPoints    - The points in the join
4909 
4910   Level: intermediate
4911 
4912   Fortran Notes:
4913   `converedPoints` must be declared with
4914 .vb
4915   PetscInt, pointer :: coveredPoints(:)
4916 .ve
4917 
4918   Pass `PETSC_NULL_INTEGER` for `numCoveredPoints` if it is not needed
4919 
4920 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4921 @*/
4922 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4923 {
4924   PetscFunctionBegin;
4925   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4926   if (points) PetscAssertPointer(points, 3);
4927   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4928   PetscAssertPointer(coveredPoints, 5);
4929   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4930   if (numCoveredPoints) *numCoveredPoints = 0;
4931   PetscFunctionReturn(PETSC_SUCCESS);
4932 }
4933 
4934 /*@C
4935   DMPlexGetFullJoin - Get an array for the join of the set of points
4936 
4937   Not Collective
4938 
4939   Input Parameters:
4940 + dm        - The `DMPLEX` object
4941 . numPoints - The number of input points for the join
4942 - points    - The input points, its length is `numPoints`
4943 
4944   Output Parameters:
4945 + numCoveredPoints - The number of points in the join
4946 - coveredPoints    - The points in the join, its length is `numCoveredPoints`
4947 
4948   Level: intermediate
4949 
4950   Fortran Notes:
4951 .vb
4952   PetscInt, pointer :: coveredPints(:)
4953 .ve
4954 
4955 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4956 @*/
4957 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4958 {
4959   PetscInt *offsets, **closures;
4960   PetscInt *join[2];
4961   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4962   PetscInt  p, d, c, m, ms;
4963 
4964   PetscFunctionBegin;
4965   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4966   PetscAssertPointer(points, 3);
4967   PetscAssertPointer(numCoveredPoints, 4);
4968   PetscAssertPointer(coveredPoints, 5);
4969 
4970   PetscCall(DMPlexGetDepth(dm, &depth));
4971   PetscCall(PetscCalloc1(numPoints, &closures));
4972   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4973   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4974   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4975   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4976   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4977 
4978   for (p = 0; p < numPoints; ++p) {
4979     PetscInt closureSize;
4980 
4981     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4982 
4983     offsets[p * (depth + 2) + 0] = 0;
4984     for (d = 0; d < depth + 1; ++d) {
4985       PetscInt pStart, pEnd, i;
4986 
4987       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4988       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4989         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4990           offsets[p * (depth + 2) + d + 1] = i;
4991           break;
4992         }
4993       }
4994       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4995     }
4996     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);
4997   }
4998   for (d = 0; d < depth + 1; ++d) {
4999     PetscInt dof;
5000 
5001     /* Copy in support of first point */
5002     dof = offsets[d + 1] - offsets[d];
5003     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
5004     /* Check each successive cone */
5005     for (p = 1; p < numPoints && joinSize; ++p) {
5006       PetscInt newJoinSize = 0;
5007 
5008       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
5009       for (c = 0; c < dof; ++c) {
5010         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
5011 
5012         for (m = 0; m < joinSize; ++m) {
5013           if (point == join[i][m]) {
5014             join[1 - i][newJoinSize++] = point;
5015             break;
5016           }
5017         }
5018       }
5019       joinSize = newJoinSize;
5020       i        = 1 - i;
5021     }
5022     if (joinSize) break;
5023   }
5024   *numCoveredPoints = joinSize;
5025   *coveredPoints    = join[i];
5026   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
5027   PetscCall(PetscFree(closures));
5028   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
5029   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
5030   PetscFunctionReturn(PETSC_SUCCESS);
5031 }
5032 
5033 /*@C
5034   DMPlexGetMeet - Get an array for the meet of the set of points
5035 
5036   Not Collective
5037 
5038   Input Parameters:
5039 + dm        - The `DMPLEX` object
5040 . numPoints - The number of input points for the meet
5041 - points    - The input points, of length `numPoints`
5042 
5043   Output Parameters:
5044 + numCoveringPoints - The number of points in the meet
5045 - coveringPoints    - The points in the meet, of length `numCoveringPoints`
5046 
5047   Level: intermediate
5048 
5049   Note:
5050   Currently, this is restricted to a single level meet
5051 
5052   Fortran Note:
5053   `coveringPoints` must be declared with
5054 .vb
5055   PetscInt, pointer :: coveringPoints(:)
5056 .ve
5057 
5058 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
5059 @*/
5060 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt *coveringPoints[])
5061 {
5062   DM_Plex  *mesh = (DM_Plex *)dm->data;
5063   PetscInt *meet[2];
5064   PetscInt  meetSize, i = 0;
5065   PetscInt  dof, off, p, c, m;
5066   PetscInt  maxConeSize;
5067 
5068   PetscFunctionBegin;
5069   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5070   PetscAssertPointer(points, 3);
5071   PetscAssertPointer(numCoveringPoints, 4);
5072   PetscAssertPointer(coveringPoints, 5);
5073   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
5074   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
5075   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
5076   /* Copy in cone of first point */
5077   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
5078   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
5079   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
5080   /* Check each successive cone */
5081   for (p = 1; p < numPoints; ++p) {
5082     PetscInt newMeetSize = 0;
5083 
5084     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
5085     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
5086     for (c = 0; c < dof; ++c) {
5087       const PetscInt point = mesh->cones[off + c];
5088 
5089       for (m = 0; m < meetSize; ++m) {
5090         if (point == meet[i][m]) {
5091           meet[1 - i][newMeetSize++] = point;
5092           break;
5093         }
5094       }
5095     }
5096     meetSize = newMeetSize;
5097     i        = 1 - i;
5098   }
5099   *numCoveringPoints = meetSize;
5100   *coveringPoints    = meet[i];
5101   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
5102   PetscFunctionReturn(PETSC_SUCCESS);
5103 }
5104 
5105 /*@C
5106   DMPlexRestoreMeet - Restore an array for the meet of the set of points obtained with `DMPlexGetMeet()`
5107 
5108   Not Collective
5109 
5110   Input Parameters:
5111 + dm        - The `DMPLEX` object
5112 . numPoints - The number of input points for the meet
5113 - points    - The input points
5114 
5115   Output Parameters:
5116 + numCoveredPoints - The number of points in the meet
5117 - coveredPoints    - The points in the meet
5118 
5119   Level: intermediate
5120 
5121 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
5122 @*/
5123 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
5124 {
5125   PetscFunctionBegin;
5126   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5127   if (points) PetscAssertPointer(points, 3);
5128   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
5129   PetscAssertPointer(coveredPoints, 5);
5130   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
5131   if (numCoveredPoints) *numCoveredPoints = 0;
5132   PetscFunctionReturn(PETSC_SUCCESS);
5133 }
5134 
5135 /*@C
5136   DMPlexGetFullMeet - Get an array for the meet of the set of points
5137 
5138   Not Collective
5139 
5140   Input Parameters:
5141 + dm        - The `DMPLEX` object
5142 . numPoints - The number of input points for the meet
5143 - points    - The input points, of length  `numPoints`
5144 
5145   Output Parameters:
5146 + numCoveredPoints - The number of points in the meet
5147 - coveredPoints    - The points in the meet, of length  `numCoveredPoints`
5148 
5149   Level: intermediate
5150 
5151   Fortran Notes:
5152   `coveredPoints` must be declared with
5153 .vb
5154   PetscInt, pointer :: coveredPoints(:)
5155 .ve
5156 
5157 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
5158 @*/
5159 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
5160 {
5161   PetscInt *offsets, **closures;
5162   PetscInt *meet[2];
5163   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
5164   PetscInt  p, h, c, m, mc;
5165 
5166   PetscFunctionBegin;
5167   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5168   PetscAssertPointer(points, 3);
5169   PetscAssertPointer(numCoveredPoints, 4);
5170   PetscAssertPointer(coveredPoints, 5);
5171 
5172   PetscCall(DMPlexGetDepth(dm, &height));
5173   PetscCall(PetscMalloc1(numPoints, &closures));
5174   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5175   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
5176   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
5177   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
5178   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
5179 
5180   for (p = 0; p < numPoints; ++p) {
5181     PetscInt closureSize;
5182 
5183     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
5184 
5185     offsets[p * (height + 2) + 0] = 0;
5186     for (h = 0; h < height + 1; ++h) {
5187       PetscInt pStart, pEnd, i;
5188 
5189       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
5190       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
5191         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
5192           offsets[p * (height + 2) + h + 1] = i;
5193           break;
5194         }
5195       }
5196       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
5197     }
5198     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);
5199   }
5200   for (h = 0; h < height + 1; ++h) {
5201     PetscInt dof;
5202 
5203     /* Copy in cone of first point */
5204     dof = offsets[h + 1] - offsets[h];
5205     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
5206     /* Check each successive cone */
5207     for (p = 1; p < numPoints && meetSize; ++p) {
5208       PetscInt newMeetSize = 0;
5209 
5210       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
5211       for (c = 0; c < dof; ++c) {
5212         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
5213 
5214         for (m = 0; m < meetSize; ++m) {
5215           if (point == meet[i][m]) {
5216             meet[1 - i][newMeetSize++] = point;
5217             break;
5218           }
5219         }
5220       }
5221       meetSize = newMeetSize;
5222       i        = 1 - i;
5223     }
5224     if (meetSize) break;
5225   }
5226   *numCoveredPoints = meetSize;
5227   *coveredPoints    = meet[i];
5228   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
5229   PetscCall(PetscFree(closures));
5230   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5231   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
5232   PetscFunctionReturn(PETSC_SUCCESS);
5233 }
5234 
5235 /*@
5236   DMPlexEqual - Determine if two `DM` have the same topology
5237 
5238   Not Collective
5239 
5240   Input Parameters:
5241 + dmA - A `DMPLEX` object
5242 - dmB - A `DMPLEX` object
5243 
5244   Output Parameter:
5245 . equal - `PETSC_TRUE` if the topologies are identical
5246 
5247   Level: intermediate
5248 
5249   Note:
5250   We are not solving graph isomorphism, so we do not permute.
5251 
5252 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5253 @*/
5254 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
5255 {
5256   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
5257 
5258   PetscFunctionBegin;
5259   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
5260   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
5261   PetscAssertPointer(equal, 3);
5262 
5263   *equal = PETSC_FALSE;
5264   PetscCall(DMPlexGetDepth(dmA, &depth));
5265   PetscCall(DMPlexGetDepth(dmB, &depthB));
5266   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
5267   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
5268   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
5269   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
5270   for (p = pStart; p < pEnd; ++p) {
5271     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
5272     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
5273 
5274     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
5275     PetscCall(DMPlexGetCone(dmA, p, &cone));
5276     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
5277     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
5278     PetscCall(DMPlexGetCone(dmB, p, &coneB));
5279     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
5280     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5281     for (c = 0; c < coneSize; ++c) {
5282       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5283       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5284     }
5285     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
5286     PetscCall(DMPlexGetSupport(dmA, p, &support));
5287     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
5288     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
5289     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5290     for (s = 0; s < supportSize; ++s) {
5291       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
5292     }
5293   }
5294   *equal = PETSC_TRUE;
5295   PetscFunctionReturn(PETSC_SUCCESS);
5296 }
5297 
5298 /*@
5299   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
5300 
5301   Not Collective
5302 
5303   Input Parameters:
5304 + dm         - The `DMPLEX`
5305 . cellDim    - The cell dimension
5306 - numCorners - The number of vertices on a cell
5307 
5308   Output Parameter:
5309 . numFaceVertices - The number of vertices on a face
5310 
5311   Level: developer
5312 
5313   Note:
5314   Of course this can only work for a restricted set of symmetric shapes
5315 
5316 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5317 @*/
5318 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
5319 {
5320   MPI_Comm comm;
5321 
5322   PetscFunctionBegin;
5323   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
5324   PetscAssertPointer(numFaceVertices, 4);
5325   switch (cellDim) {
5326   case 0:
5327     *numFaceVertices = 0;
5328     break;
5329   case 1:
5330     *numFaceVertices = 1;
5331     break;
5332   case 2:
5333     switch (numCorners) {
5334     case 3:                 /* triangle */
5335       *numFaceVertices = 2; /* Edge has 2 vertices */
5336       break;
5337     case 4:                 /* quadrilateral */
5338       *numFaceVertices = 2; /* Edge has 2 vertices */
5339       break;
5340     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
5341       *numFaceVertices = 3; /* Edge has 3 vertices */
5342       break;
5343     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
5344       *numFaceVertices = 3; /* Edge has 3 vertices */
5345       break;
5346     default:
5347       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5348     }
5349     break;
5350   case 3:
5351     switch (numCorners) {
5352     case 4:                 /* tetradehdron */
5353       *numFaceVertices = 3; /* Face has 3 vertices */
5354       break;
5355     case 6:                 /* tet cohesive cells */
5356       *numFaceVertices = 4; /* Face has 4 vertices */
5357       break;
5358     case 8:                 /* hexahedron */
5359       *numFaceVertices = 4; /* Face has 4 vertices */
5360       break;
5361     case 9:                 /* tet cohesive Lagrange cells */
5362       *numFaceVertices = 6; /* Face has 6 vertices */
5363       break;
5364     case 10:                /* quadratic tetrahedron */
5365       *numFaceVertices = 6; /* Face has 6 vertices */
5366       break;
5367     case 12:                /* hex cohesive Lagrange cells */
5368       *numFaceVertices = 6; /* Face has 6 vertices */
5369       break;
5370     case 18:                /* quadratic tet cohesive Lagrange cells */
5371       *numFaceVertices = 6; /* Face has 6 vertices */
5372       break;
5373     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
5374       *numFaceVertices = 9; /* Face has 9 vertices */
5375       break;
5376     default:
5377       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5378     }
5379     break;
5380   default:
5381     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
5382   }
5383   PetscFunctionReturn(PETSC_SUCCESS);
5384 }
5385 
5386 /*@
5387   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
5388 
5389   Not Collective
5390 
5391   Input Parameter:
5392 . dm - The `DMPLEX` object
5393 
5394   Output Parameter:
5395 . depthLabel - The `DMLabel` recording point depth
5396 
5397   Level: developer
5398 
5399 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
5400 @*/
5401 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
5402 {
5403   PetscFunctionBegin;
5404   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5405   PetscAssertPointer(depthLabel, 2);
5406   *depthLabel = dm->depthLabel;
5407   PetscFunctionReturn(PETSC_SUCCESS);
5408 }
5409 
5410 /*@
5411   DMPlexGetDepth - Get the depth of the DAG representing this mesh
5412 
5413   Not Collective
5414 
5415   Input Parameter:
5416 . dm - The `DMPLEX` object
5417 
5418   Output Parameter:
5419 . depth - The number of strata (breadth first levels) in the DAG
5420 
5421   Level: developer
5422 
5423   Notes:
5424   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
5425 
5426   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
5427 
5428   An empty mesh gives -1.
5429 
5430 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
5431 @*/
5432 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
5433 {
5434   DM_Plex *mesh = (DM_Plex *)dm->data;
5435   DMLabel  label;
5436   PetscInt d = -1;
5437 
5438   PetscFunctionBegin;
5439   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5440   PetscAssertPointer(depth, 2);
5441   if (mesh->tr) {
5442     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
5443   } else {
5444     PetscCall(DMPlexGetDepthLabel(dm, &label));
5445     // Allow missing depths
5446     if (label) PetscCall(DMLabelGetValueBounds(label, NULL, &d));
5447     *depth = d;
5448   }
5449   PetscFunctionReturn(PETSC_SUCCESS);
5450 }
5451 
5452 /*@
5453   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
5454 
5455   Not Collective
5456 
5457   Input Parameters:
5458 + dm    - The `DMPLEX` object
5459 - depth - The requested depth
5460 
5461   Output Parameters:
5462 + start - The first point at this `depth`
5463 - end   - One beyond the last point at this `depth`
5464 
5465   Level: developer
5466 
5467   Notes:
5468   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5469   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5470   higher dimension, e.g., "edges".
5471 
5472 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5473 @*/
5474 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PeOp PetscInt *start, PeOp PetscInt *end)
5475 {
5476   DM_Plex *mesh = (DM_Plex *)dm->data;
5477   DMLabel  label;
5478   PetscInt pStart, pEnd;
5479 
5480   PetscFunctionBegin;
5481   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5482   if (start) {
5483     PetscAssertPointer(start, 3);
5484     *start = 0;
5485   }
5486   if (end) {
5487     PetscAssertPointer(end, 4);
5488     *end = 0;
5489   }
5490   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5491   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5492   if (depth < 0) {
5493     if (start) *start = pStart;
5494     if (end) *end = pEnd;
5495     PetscFunctionReturn(PETSC_SUCCESS);
5496   }
5497   if (mesh->tr) {
5498     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5499   } else {
5500     PetscCall(DMPlexGetDepthLabel(dm, &label));
5501     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5502     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5503   }
5504   PetscFunctionReturn(PETSC_SUCCESS);
5505 }
5506 
5507 /*@
5508   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5509 
5510   Not Collective
5511 
5512   Input Parameters:
5513 + dm     - The `DMPLEX` object
5514 - height - The requested height
5515 
5516   Output Parameters:
5517 + start - The first point at this `height`
5518 - end   - One beyond the last point at this `height`
5519 
5520   Level: developer
5521 
5522   Notes:
5523   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5524   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5525   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5526 
5527 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5528 @*/
5529 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PeOp PetscInt *start, PeOp PetscInt *end)
5530 {
5531   DMLabel  label;
5532   PetscInt depth, pStart, pEnd;
5533 
5534   PetscFunctionBegin;
5535   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5536   if (start) {
5537     PetscAssertPointer(start, 3);
5538     *start = 0;
5539   }
5540   if (end) {
5541     PetscAssertPointer(end, 4);
5542     *end = 0;
5543   }
5544   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5545   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5546   if (height < 0) {
5547     if (start) *start = pStart;
5548     if (end) *end = pEnd;
5549     PetscFunctionReturn(PETSC_SUCCESS);
5550   }
5551   PetscCall(DMPlexGetDepthLabel(dm, &label));
5552   if (label) PetscCall(DMLabelGetNumValues(label, &depth));
5553   else PetscCall(DMGetDimension(dm, &depth));
5554   PetscCheck(depth >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Depth not yet computed");
5555   PetscCall(DMPlexGetDepthStratum(dm, depth - 1 - height, start, end));
5556   PetscFunctionReturn(PETSC_SUCCESS);
5557 }
5558 
5559 /*@
5560   DMPlexGetPointDepth - Get the `depth` of a given point
5561 
5562   Not Collective
5563 
5564   Input Parameters:
5565 + dm    - The `DMPLEX` object
5566 - point - The point
5567 
5568   Output Parameter:
5569 . depth - The depth of the `point`
5570 
5571   Level: intermediate
5572 
5573 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5574 @*/
5575 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5576 {
5577   PetscFunctionBegin;
5578   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5579   PetscAssertPointer(depth, 3);
5580   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5581   PetscFunctionReturn(PETSC_SUCCESS);
5582 }
5583 
5584 /*@
5585   DMPlexGetPointHeight - Get the `height` of a given point
5586 
5587   Not Collective
5588 
5589   Input Parameters:
5590 + dm    - The `DMPLEX` object
5591 - point - The point
5592 
5593   Output Parameter:
5594 . height - The height of the `point`
5595 
5596   Level: intermediate
5597 
5598 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5599 @*/
5600 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5601 {
5602   PetscInt n, pDepth;
5603 
5604   PetscFunctionBegin;
5605   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5606   PetscAssertPointer(height, 3);
5607   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5608   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5609   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5610   PetscFunctionReturn(PETSC_SUCCESS);
5611 }
5612 
5613 /*@
5614   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5615 
5616   Not Collective
5617 
5618   Input Parameter:
5619 . dm - The `DMPLEX` object
5620 
5621   Output Parameter:
5622 . celltypeLabel - The `DMLabel` recording cell polytope type
5623 
5624   Level: developer
5625 
5626   Note:
5627   This function will trigger automatica computation of cell types. This can be disabled by calling
5628   `DMCreateLabel`(dm, "celltype") beforehand.
5629 
5630 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5631 @*/
5632 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5633 {
5634   PetscFunctionBegin;
5635   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5636   PetscAssertPointer(celltypeLabel, 2);
5637   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5638   *celltypeLabel = dm->celltypeLabel;
5639   PetscFunctionReturn(PETSC_SUCCESS);
5640 }
5641 
5642 /*@
5643   DMPlexGetCellType - Get the polytope type of a given cell
5644 
5645   Not Collective
5646 
5647   Input Parameters:
5648 + dm   - The `DMPLEX` object
5649 - cell - The cell
5650 
5651   Output Parameter:
5652 . celltype - The polytope type of the cell
5653 
5654   Level: intermediate
5655 
5656 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5657 @*/
5658 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5659 {
5660   DM_Plex *mesh = (DM_Plex *)dm->data;
5661   DMLabel  label;
5662   PetscInt ct;
5663 
5664   PetscFunctionBegin;
5665   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5666   PetscAssertPointer(celltype, 3);
5667   if (mesh->tr) {
5668     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5669   } else {
5670     PetscInt pStart, pEnd;
5671 
5672     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5673     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5674       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5675       if (pEnd <= pStart) {
5676         *celltype = DM_POLYTOPE_UNKNOWN;
5677         PetscFunctionReturn(PETSC_SUCCESS);
5678       }
5679       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5680       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5681       for (PetscInt p = pStart; p < pEnd; p++) {
5682         PetscCall(DMLabelGetValue(label, p, &ct));
5683         mesh->cellTypes[p - pStart].value_as_uint8 = (uint8_t)ct;
5684       }
5685     }
5686     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5687     if (PetscDefined(USE_DEBUG)) {
5688       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5689       PetscCall(DMLabelGetValue(label, cell, &ct));
5690       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5691       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5692     }
5693   }
5694   PetscFunctionReturn(PETSC_SUCCESS);
5695 }
5696 
5697 /*@
5698   DMPlexSetCellType - Set the polytope type of a given cell
5699 
5700   Not Collective
5701 
5702   Input Parameters:
5703 + dm       - The `DMPLEX` object
5704 . cell     - The cell
5705 - celltype - The polytope type of the cell
5706 
5707   Level: advanced
5708 
5709   Note:
5710   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5711   is executed. This function will override the computed type. However, if automatic classification will not succeed
5712   and a user wants to manually specify all types, the classification must be disabled by calling
5713   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5714 
5715 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5716 @*/
5717 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5718 {
5719   DM_Plex *mesh = (DM_Plex *)dm->data;
5720   DMLabel  label;
5721   PetscInt pStart, pEnd;
5722 
5723   PetscFunctionBegin;
5724   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5725   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5726   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5727   PetscCall(DMLabelSetValue(label, cell, celltype));
5728   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5729   mesh->cellTypes[cell - pStart].value_as_uint8 = (uint8_t)celltype;
5730   PetscFunctionReturn(PETSC_SUCCESS);
5731 }
5732 
5733 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5734 {
5735   PetscSection section;
5736   PetscInt     maxHeight;
5737   const char  *prefix;
5738 
5739   PetscFunctionBegin;
5740   PetscCall(DMClone(dm, cdm));
5741   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5742   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5743   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5744   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5745   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5746   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5747   PetscCall(DMSetLocalSection(*cdm, section));
5748   PetscCall(PetscSectionDestroy(&section));
5749 
5750   PetscCall(DMSetNumFields(*cdm, 1));
5751   PetscCall(DMCreateDS(*cdm));
5752   (*cdm)->cloneOpts = PETSC_TRUE;
5753   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5754   PetscFunctionReturn(PETSC_SUCCESS);
5755 }
5756 
5757 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5758 {
5759   Vec coordsLocal, cellCoordsLocal;
5760   DM  coordsDM, cellCoordsDM;
5761 
5762   PetscFunctionBegin;
5763   *field = NULL;
5764   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5765   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5766   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5767   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5768   if (coordsLocal && coordsDM) {
5769     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5770     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5771   }
5772   PetscFunctionReturn(PETSC_SUCCESS);
5773 }
5774 
5775 /*@
5776   DMPlexGetConeSection - Return a section which describes the layout of cone data
5777 
5778   Not Collective
5779 
5780   Input Parameter:
5781 . dm - The `DMPLEX` object
5782 
5783   Output Parameter:
5784 . section - The `PetscSection` object
5785 
5786   Level: developer
5787 
5788 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5789 @*/
5790 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5791 {
5792   DM_Plex *mesh = (DM_Plex *)dm->data;
5793 
5794   PetscFunctionBegin;
5795   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5796   if (section) *section = mesh->coneSection;
5797   PetscFunctionReturn(PETSC_SUCCESS);
5798 }
5799 
5800 /*@
5801   DMPlexGetSupportSection - Return a section which describes the layout of support data
5802 
5803   Not Collective
5804 
5805   Input Parameter:
5806 . dm - The `DMPLEX` object
5807 
5808   Output Parameter:
5809 . section - The `PetscSection` object
5810 
5811   Level: developer
5812 
5813 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5814 @*/
5815 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5816 {
5817   DM_Plex *mesh = (DM_Plex *)dm->data;
5818 
5819   PetscFunctionBegin;
5820   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5821   if (section) *section = mesh->supportSection;
5822   PetscFunctionReturn(PETSC_SUCCESS);
5823 }
5824 
5825 /*@C
5826   DMPlexGetCones - Return cone data
5827 
5828   Not Collective
5829 
5830   Input Parameter:
5831 . dm - The `DMPLEX` object
5832 
5833   Output Parameter:
5834 . cones - The cone for each point
5835 
5836   Level: developer
5837 
5838 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5839 @*/
5840 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5841 {
5842   DM_Plex *mesh = (DM_Plex *)dm->data;
5843 
5844   PetscFunctionBegin;
5845   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5846   if (cones) *cones = mesh->cones;
5847   PetscFunctionReturn(PETSC_SUCCESS);
5848 }
5849 
5850 /*@C
5851   DMPlexGetConeOrientations - Return cone orientation data
5852 
5853   Not Collective
5854 
5855   Input Parameter:
5856 . dm - The `DMPLEX` object
5857 
5858   Output Parameter:
5859 . coneOrientations - The array of cone orientations for all points
5860 
5861   Level: developer
5862 
5863   Notes:
5864   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points
5865   as returned by `DMPlexGetConeOrientation()`.
5866 
5867   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5868 
5869 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5870 @*/
5871 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5872 {
5873   DM_Plex *mesh = (DM_Plex *)dm->data;
5874 
5875   PetscFunctionBegin;
5876   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5877   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5878   PetscFunctionReturn(PETSC_SUCCESS);
5879 }
5880 
5881 /* FEM Support */
5882 
5883 PetscErrorCode DMPlexGetAllCells_Internal(DM plex, IS *cellIS)
5884 {
5885   PetscInt depth;
5886 
5887   PetscFunctionBegin;
5888   PetscCall(DMPlexGetDepth(plex, &depth));
5889   PetscCall(DMGetStratumIS(plex, "dim", depth, cellIS));
5890   if (!*cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, cellIS));
5891   PetscFunctionReturn(PETSC_SUCCESS);
5892 }
5893 
5894 PetscErrorCode DMPlexGetAllFaces_Internal(DM plex, IS *faceIS)
5895 {
5896   PetscInt depth;
5897 
5898   PetscFunctionBegin;
5899   PetscCall(DMPlexGetDepth(plex, &depth));
5900   PetscCall(DMGetStratumIS(plex, "dim", depth - 1, faceIS));
5901   if (!*faceIS) PetscCall(DMGetStratumIS(plex, "depth", depth - 1, faceIS));
5902   PetscFunctionReturn(PETSC_SUCCESS);
5903 }
5904 
5905 /*
5906  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5907  representing a line in the section.
5908 */
5909 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(DM dm, PetscSection section, PetscInt field, PetscInt line, PetscInt *Nc, PetscInt *k, PetscBool *continuous, PetscBool *tensor)
5910 {
5911   PetscObject  obj;
5912   PetscClassId id;
5913   PetscFE      fe = NULL;
5914 
5915   PetscFunctionBeginHot;
5916   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5917   PetscCall(DMGetField(dm, field, NULL, &obj));
5918   PetscCall(PetscObjectGetClassId(obj, &id));
5919   if (id == PETSCFE_CLASSID) fe = (PetscFE)obj;
5920 
5921   if (!fe) {
5922     /* Assume the full interpolated mesh is in the chart; lines in particular */
5923     /* An order k SEM disc has k-1 dofs on an edge */
5924     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5925     *k = *k / *Nc + 1;
5926   } else {
5927     PetscInt       dual_space_size, dim;
5928     PetscDualSpace dsp;
5929 
5930     PetscCall(DMGetDimension(dm, &dim));
5931     PetscCall(PetscFEGetDualSpace(fe, &dsp));
5932     PetscCall(PetscDualSpaceGetDimension(dsp, &dual_space_size));
5933     *k = (PetscInt)PetscCeilReal(PetscPowReal(dual_space_size / *Nc, 1.0 / dim)) - 1;
5934     PetscCall(PetscDualSpaceLagrangeGetContinuity(dsp, continuous));
5935     PetscCall(PetscDualSpaceLagrangeGetTensor(dsp, tensor));
5936   }
5937   PetscFunctionReturn(PETSC_SUCCESS);
5938 }
5939 
5940 static PetscErrorCode GetFieldSize_Private(PetscInt dim, PetscInt k, PetscBool tensor, PetscInt *dof)
5941 {
5942   PetscFunctionBeginHot;
5943   if (tensor) {
5944     *dof = PetscPowInt(k + 1, dim);
5945   } else {
5946     switch (dim) {
5947     case 1:
5948       *dof = k + 1;
5949       break;
5950     case 2:
5951       *dof = ((k + 1) * (k + 2)) / 2;
5952       break;
5953     case 3:
5954       *dof = ((k + 1) * (k + 2) * (k + 3)) / 6;
5955       break;
5956     default:
5957       *dof = 0;
5958     }
5959   }
5960   PetscFunctionReturn(PETSC_SUCCESS);
5961 }
5962 
5963 /*@
5964   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5965   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5966   section provided (or the section of the `DM`).
5967 
5968   Input Parameters:
5969 + dm      - The `DM`
5970 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5971 - section - The `PetscSection` to reorder, or `NULL` for the default section
5972 
5973   Example:
5974   A typical interpolated single-quad mesh might order points as
5975 .vb
5976   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5977 
5978   v4 -- e6 -- v3
5979   |           |
5980   e7    c0    e8
5981   |           |
5982   v1 -- e5 -- v2
5983 .ve
5984 
5985   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5986   dofs in the order of points, e.g.,
5987 .vb
5988     c0 -> [0,1,2,3]
5989     v1 -> [4]
5990     ...
5991     e5 -> [8, 9]
5992 .ve
5993 
5994   which corresponds to the dofs
5995 .vb
5996     6   10  11  7
5997     13  2   3   15
5998     12  0   1   14
5999     4   8   9   5
6000 .ve
6001 
6002   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
6003 .vb
6004   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
6005 .ve
6006 
6007   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
6008 .vb
6009    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
6010 .ve
6011 
6012   Level: developer
6013 
6014   Notes:
6015   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
6016   degree of the basis.
6017 
6018   This is required to run with libCEED.
6019 
6020 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
6021 @*/
6022 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
6023 {
6024   DMLabel   label;
6025   PetscInt  dim, depth = -1, eStart = -1, Nf;
6026   PetscBool continuous = PETSC_TRUE, tensor = PETSC_TRUE;
6027 
6028   PetscFunctionBegin;
6029   PetscCall(DMGetDimension(dm, &dim));
6030   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
6031   if (point < 0) {
6032     PetscInt sStart, sEnd;
6033 
6034     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
6035     point = sEnd - sStart ? sStart : point;
6036   }
6037   PetscCall(DMPlexGetDepthLabel(dm, &label));
6038   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
6039   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6040   if (depth == 1) {
6041     eStart = point;
6042   } else if (depth == dim) {
6043     const PetscInt *cone;
6044 
6045     PetscCall(DMPlexGetCone(dm, point, &cone));
6046     if (dim == 2) eStart = cone[0];
6047     else if (dim == 3) {
6048       const PetscInt *cone2;
6049       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
6050       eStart = cone2[0];
6051     } 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);
6052   } 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);
6053 
6054   PetscCall(PetscSectionGetNumFields(section, &Nf));
6055   for (PetscInt d = 1; d <= dim; d++) {
6056     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
6057     PetscInt *perm;
6058 
6059     for (f = 0; f < Nf; ++f) {
6060       PetscInt dof;
6061 
6062       PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6063       PetscCheck(dim == 1 || tensor || !continuous, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Continuous field %" PetscInt_FMT " must have a tensor product discretization", f);
6064       if (!continuous && d < dim) continue;
6065       PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6066       size += dof * Nc;
6067     }
6068     PetscCall(PetscMalloc1(size, &perm));
6069     for (f = 0; f < Nf; ++f) {
6070       switch (d) {
6071       case 1:
6072         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6073         if (!continuous && d < dim) continue;
6074         /*
6075          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
6076          We want              [ vtx0; edge of length k-1; vtx1 ]
6077          */
6078         if (continuous) {
6079           for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
6080           for (i = 0; i < k - 1; i++)
6081             for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
6082           for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
6083           foffset = offset;
6084         } else {
6085           PetscInt dof;
6086 
6087           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6088           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6089           foffset = offset;
6090         }
6091         break;
6092       case 2:
6093         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
6094         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6095         if (!continuous && d < dim) continue;
6096         /* The SEM order is
6097 
6098          v_lb, {e_b}, v_rb,
6099          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
6100          v_lt, reverse {e_t}, v_rt
6101          */
6102         if (continuous) {
6103           const PetscInt of   = 0;
6104           const PetscInt oeb  = of + PetscSqr(k - 1);
6105           const PetscInt oer  = oeb + (k - 1);
6106           const PetscInt oet  = oer + (k - 1);
6107           const PetscInt oel  = oet + (k - 1);
6108           const PetscInt ovlb = oel + (k - 1);
6109           const PetscInt ovrb = ovlb + 1;
6110           const PetscInt ovrt = ovrb + 1;
6111           const PetscInt ovlt = ovrt + 1;
6112           PetscInt       o;
6113 
6114           /* bottom */
6115           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
6116           for (o = oeb; o < oer; ++o)
6117             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6118           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
6119           /* middle */
6120           for (i = 0; i < k - 1; ++i) {
6121             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
6122             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
6123               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6124             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
6125           }
6126           /* top */
6127           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
6128           for (o = oel - 1; o >= oet; --o)
6129             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6130           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
6131           foffset = offset;
6132         } else {
6133           PetscInt dof;
6134 
6135           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6136           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6137           foffset = offset;
6138         }
6139         break;
6140       case 3:
6141         /* The original hex closure is
6142 
6143          {c,
6144          f_b, f_t, f_f, f_b, f_r, f_l,
6145          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
6146          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
6147          */
6148         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6149         if (!continuous && d < dim) continue;
6150         /* The SEM order is
6151          Bottom Slice
6152          v_blf, {e^{(k-1)-n}_bf}, v_brf,
6153          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
6154          v_blb, {e_bb}, v_brb,
6155 
6156          Middle Slice (j)
6157          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
6158          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
6159          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
6160 
6161          Top Slice
6162          v_tlf, {e_tf}, v_trf,
6163          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
6164          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
6165          */
6166         if (continuous) {
6167           const PetscInt oc    = 0;
6168           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
6169           const PetscInt oft   = ofb + PetscSqr(k - 1);
6170           const PetscInt off   = oft + PetscSqr(k - 1);
6171           const PetscInt ofk   = off + PetscSqr(k - 1);
6172           const PetscInt ofr   = ofk + PetscSqr(k - 1);
6173           const PetscInt ofl   = ofr + PetscSqr(k - 1);
6174           const PetscInt oebl  = ofl + PetscSqr(k - 1);
6175           const PetscInt oebb  = oebl + (k - 1);
6176           const PetscInt oebr  = oebb + (k - 1);
6177           const PetscInt oebf  = oebr + (k - 1);
6178           const PetscInt oetf  = oebf + (k - 1);
6179           const PetscInt oetr  = oetf + (k - 1);
6180           const PetscInt oetb  = oetr + (k - 1);
6181           const PetscInt oetl  = oetb + (k - 1);
6182           const PetscInt oerf  = oetl + (k - 1);
6183           const PetscInt oelf  = oerf + (k - 1);
6184           const PetscInt oelb  = oelf + (k - 1);
6185           const PetscInt oerb  = oelb + (k - 1);
6186           const PetscInt ovblf = oerb + (k - 1);
6187           const PetscInt ovblb = ovblf + 1;
6188           const PetscInt ovbrb = ovblb + 1;
6189           const PetscInt ovbrf = ovbrb + 1;
6190           const PetscInt ovtlf = ovbrf + 1;
6191           const PetscInt ovtrf = ovtlf + 1;
6192           const PetscInt ovtrb = ovtrf + 1;
6193           const PetscInt ovtlb = ovtrb + 1;
6194           PetscInt       o, n;
6195 
6196           /* Bottom Slice */
6197           /*   bottom */
6198           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
6199           for (o = oetf - 1; o >= oebf; --o)
6200             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6201           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
6202           /*   middle */
6203           for (i = 0; i < k - 1; ++i) {
6204             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
6205             for (n = 0; n < k - 1; ++n) {
6206               o = ofb + n * (k - 1) + i;
6207               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6208             }
6209             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
6210           }
6211           /*   top */
6212           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
6213           for (o = oebb; o < oebr; ++o)
6214             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6215           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
6216 
6217           /* Middle Slice */
6218           for (j = 0; j < k - 1; ++j) {
6219             /*   bottom */
6220             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
6221             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++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] = (oerf + j) * Nc + c + foffset;
6224             /*   middle */
6225             for (i = 0; i < k - 1; ++i) {
6226               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
6227               for (n = 0; n < k - 1; ++n)
6228                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
6229               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
6230             }
6231             /*   top */
6232             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
6233             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
6234               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6235             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
6236           }
6237 
6238           /* Top Slice */
6239           /*   bottom */
6240           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
6241           for (o = oetf; o < oetr; ++o)
6242             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6243           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
6244           /*   middle */
6245           for (i = 0; i < k - 1; ++i) {
6246             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
6247             for (n = 0; n < k - 1; ++n)
6248               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
6249             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
6250           }
6251           /*   top */
6252           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
6253           for (o = oetl - 1; o >= oetb; --o)
6254             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6255           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
6256 
6257           foffset = offset;
6258         } else {
6259           PetscInt dof;
6260 
6261           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6262           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6263           foffset = offset;
6264         }
6265         break;
6266       default:
6267         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
6268       }
6269     }
6270     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
6271     /* Check permutation */
6272     {
6273       PetscInt *check;
6274 
6275       PetscCall(PetscMalloc1(size, &check));
6276       for (i = 0; i < size; ++i) {
6277         check[i] = -1;
6278         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
6279       }
6280       for (i = 0; i < size; ++i) check[perm[i]] = i;
6281       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
6282       PetscCall(PetscFree(check));
6283     }
6284     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
6285     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
6286       PetscInt *loc_perm;
6287       PetscCall(PetscMalloc1(size * 2, &loc_perm));
6288       for (PetscInt i = 0; i < size; i++) {
6289         loc_perm[i]        = perm[i];
6290         loc_perm[size + i] = size + perm[i];
6291       }
6292       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
6293     }
6294   }
6295   PetscFunctionReturn(PETSC_SUCCESS);
6296 }
6297 
6298 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
6299 {
6300   PetscDS  prob;
6301   PetscInt depth, Nf, h;
6302   DMLabel  label;
6303 
6304   PetscFunctionBeginHot;
6305   PetscCall(DMGetDS(dm, &prob));
6306   Nf      = prob->Nf;
6307   label   = dm->depthLabel;
6308   *dspace = NULL;
6309   if (field < Nf) {
6310     PetscObject disc = prob->disc[field];
6311 
6312     if (disc->classid == PETSCFE_CLASSID) {
6313       PetscDualSpace dsp;
6314 
6315       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
6316       PetscCall(DMLabelGetNumValues(label, &depth));
6317       PetscCall(DMLabelGetValue(label, point, &h));
6318       h = depth - 1 - h;
6319       if (h) {
6320         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
6321       } else {
6322         *dspace = dsp;
6323       }
6324     }
6325   }
6326   PetscFunctionReturn(PETSC_SUCCESS);
6327 }
6328 
6329 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6330 {
6331   PetscScalar       *array;
6332   const PetscScalar *vArray;
6333   const PetscInt    *cone, *coneO;
6334   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
6335 
6336   PetscFunctionBeginHot;
6337   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6338   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6339   PetscCall(DMPlexGetCone(dm, point, &cone));
6340   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6341   if (!values || !*values) {
6342     if ((point >= pStart) && (point < pEnd)) {
6343       PetscInt dof;
6344 
6345       PetscCall(PetscSectionGetDof(section, point, &dof));
6346       size += dof;
6347     }
6348     for (p = 0; p < numPoints; ++p) {
6349       const PetscInt cp = cone[p];
6350       PetscInt       dof;
6351 
6352       if ((cp < pStart) || (cp >= pEnd)) continue;
6353       PetscCall(PetscSectionGetDof(section, cp, &dof));
6354       size += dof;
6355     }
6356     if (!values) {
6357       if (csize) *csize = size;
6358       PetscFunctionReturn(PETSC_SUCCESS);
6359     }
6360     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
6361   } else {
6362     array = *values;
6363   }
6364   size = 0;
6365   PetscCall(VecGetArrayRead(v, &vArray));
6366   if ((point >= pStart) && (point < pEnd)) {
6367     PetscInt           dof, off, d;
6368     const PetscScalar *varr;
6369 
6370     PetscCall(PetscSectionGetDof(section, point, &dof));
6371     PetscCall(PetscSectionGetOffset(section, point, &off));
6372     varr = PetscSafePointerPlusOffset(vArray, off);
6373     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6374     size += dof;
6375   }
6376   for (p = 0; p < numPoints; ++p) {
6377     const PetscInt     cp = cone[p];
6378     PetscInt           o  = coneO[p];
6379     PetscInt           dof, off, d;
6380     const PetscScalar *varr;
6381 
6382     if ((cp < pStart) || (cp >= pEnd)) continue;
6383     PetscCall(PetscSectionGetDof(section, cp, &dof));
6384     PetscCall(PetscSectionGetOffset(section, cp, &off));
6385     varr = PetscSafePointerPlusOffset(vArray, off);
6386     if (o >= 0) {
6387       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6388     } else {
6389       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
6390     }
6391     size += dof;
6392   }
6393   PetscCall(VecRestoreArrayRead(v, &vArray));
6394   if (!*values) {
6395     if (csize) *csize = size;
6396     *values = array;
6397   } else {
6398     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6399     *csize = size;
6400   }
6401   PetscFunctionReturn(PETSC_SUCCESS);
6402 }
6403 
6404 /* Compress out points not in the section */
6405 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
6406 {
6407   const PetscInt np = *numPoints;
6408   PetscInt       pStart, pEnd, p, q;
6409 
6410   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6411   for (p = 0, q = 0; p < np; ++p) {
6412     const PetscInt r = points[p * 2];
6413     if ((r >= pStart) && (r < pEnd)) {
6414       points[q * 2]     = r;
6415       points[q * 2 + 1] = points[p * 2 + 1];
6416       ++q;
6417     }
6418   }
6419   *numPoints = q;
6420   return PETSC_SUCCESS;
6421 }
6422 
6423 /* Compressed closure does not apply closure permutation */
6424 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6425 {
6426   const PetscInt *cla = NULL;
6427   PetscInt        np, *pts = NULL;
6428 
6429   PetscFunctionBeginHot;
6430   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
6431   if (!ornt && *clPoints) {
6432     PetscInt dof, off;
6433 
6434     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
6435     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
6436     PetscCall(ISGetIndices(*clPoints, &cla));
6437     np  = dof / 2;
6438     pts = PetscSafePointerPlusOffset((PetscInt *)cla, off);
6439   } else {
6440     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
6441     PetscCall(CompressPoints_Private(section, &np, pts));
6442   }
6443   *numPoints = np;
6444   *points    = pts;
6445   *clp       = cla;
6446   PetscFunctionReturn(PETSC_SUCCESS);
6447 }
6448 
6449 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6450 {
6451   PetscFunctionBeginHot;
6452   if (!*clPoints) {
6453     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
6454   } else {
6455     PetscCall(ISRestoreIndices(*clPoints, clp));
6456   }
6457   *numPoints = 0;
6458   *points    = NULL;
6459   *clSec     = NULL;
6460   *clPoints  = NULL;
6461   *clp       = NULL;
6462   PetscFunctionReturn(PETSC_SUCCESS);
6463 }
6464 
6465 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
6466 {
6467   PetscInt            offset = 0, p;
6468   const PetscInt    **perms  = NULL;
6469   const PetscScalar **flips  = NULL;
6470 
6471   PetscFunctionBeginHot;
6472   *size = 0;
6473   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6474   for (p = 0; p < numPoints; p++) {
6475     const PetscInt     point = points[2 * p];
6476     const PetscInt    *perm  = perms ? perms[p] : NULL;
6477     const PetscScalar *flip  = flips ? flips[p] : NULL;
6478     PetscInt           dof, off, d;
6479     const PetscScalar *varr;
6480 
6481     PetscCall(PetscSectionGetDof(section, point, &dof));
6482     PetscCall(PetscSectionGetOffset(section, point, &off));
6483     varr = PetscSafePointerPlusOffset(vArray, off);
6484     if (clperm) {
6485       if (perm) {
6486         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
6487       } else {
6488         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
6489       }
6490       if (flip) {
6491         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
6492       }
6493     } else {
6494       if (perm) {
6495         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
6496       } else {
6497         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
6498       }
6499       if (flip) {
6500         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
6501       }
6502     }
6503     offset += dof;
6504   }
6505   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6506   *size = offset;
6507   PetscFunctionReturn(PETSC_SUCCESS);
6508 }
6509 
6510 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[])
6511 {
6512   PetscInt offset = 0, f;
6513 
6514   PetscFunctionBeginHot;
6515   *size = 0;
6516   for (f = 0; f < numFields; ++f) {
6517     PetscInt            p;
6518     const PetscInt    **perms = NULL;
6519     const PetscScalar **flips = NULL;
6520 
6521     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6522     for (p = 0; p < numPoints; p++) {
6523       const PetscInt     point = points[2 * p];
6524       PetscInt           fdof, foff, b;
6525       const PetscScalar *varr;
6526       const PetscInt    *perm = perms ? perms[p] : NULL;
6527       const PetscScalar *flip = flips ? flips[p] : NULL;
6528 
6529       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6530       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6531       varr = &vArray[foff];
6532       if (clperm) {
6533         if (perm) {
6534           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6535         } else {
6536           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6537         }
6538         if (flip) {
6539           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6540         }
6541       } else {
6542         if (perm) {
6543           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6544         } else {
6545           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6546         }
6547         if (flip) {
6548           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6549         }
6550       }
6551       offset += fdof;
6552     }
6553     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6554   }
6555   *size = offset;
6556   PetscFunctionReturn(PETSC_SUCCESS);
6557 }
6558 
6559 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, PetscBool useClPerm, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6560 {
6561   PetscSection    clSection;
6562   IS              clPoints;
6563   PetscInt       *points = NULL;
6564   const PetscInt *clp, *perm = NULL;
6565   PetscInt        depth, numFields, numPoints, asize;
6566 
6567   PetscFunctionBeginHot;
6568   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6569   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6570   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6571   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
6572   PetscCall(DMPlexGetDepth(dm, &depth));
6573   PetscCall(PetscSectionGetNumFields(section, &numFields));
6574   if (depth == 1 && numFields < 2) {
6575     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6576     PetscFunctionReturn(PETSC_SUCCESS);
6577   }
6578   /* Get points */
6579   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6580   /* Get sizes */
6581   asize = 0;
6582   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6583     PetscInt dof;
6584     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6585     asize += dof;
6586   }
6587   if (values) {
6588     const PetscScalar *vArray;
6589     PetscInt           size;
6590 
6591     if (*values) {
6592       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);
6593     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6594     if (useClPerm) PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6595     PetscCall(VecGetArrayRead(v, &vArray));
6596     /* Get values */
6597     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6598     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6599     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6600     /* Cleanup array */
6601     PetscCall(VecRestoreArrayRead(v, &vArray));
6602   }
6603   if (csize) *csize = asize;
6604   /* Cleanup points */
6605   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6606   PetscFunctionReturn(PETSC_SUCCESS);
6607 }
6608 
6609 /*@C
6610   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6611 
6612   Not collective
6613 
6614   Input Parameters:
6615 + dm      - The `DM`
6616 . section - The section describing the layout in `v`, or `NULL` to use the default section
6617 . v       - The local vector
6618 - point   - The point in the `DM`
6619 
6620   Input/Output Parameters:
6621 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6622 - values - An array to use for the values, or *values = `NULL` to have it allocated automatically;
6623            if the user provided `NULL`, it is a borrowed array and should not be freed, use  `DMPlexVecRestoreClosure()` to return it
6624 
6625   Level: intermediate
6626 
6627   Notes:
6628   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6629   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6630   assembly function, and a user may already have allocated storage for this operation.
6631 
6632   A typical use could be
6633 .vb
6634    values = NULL;
6635    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6636    for (cl = 0; cl < clSize; ++cl) {
6637      <Compute on closure>
6638    }
6639    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6640 .ve
6641   or
6642 .vb
6643    PetscMalloc1(clMaxSize, &values);
6644    for (p = pStart; p < pEnd; ++p) {
6645      clSize = clMaxSize;
6646      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6647      for (cl = 0; cl < clSize; ++cl) {
6648        <Compute on closure>
6649      }
6650    }
6651    PetscFree(values);
6652 .ve
6653 
6654   Fortran Notes:
6655   The `csize` argument is present in the Fortran binding. Since the Fortran `values` array contains its length information this argument may not be needed.
6656   In that case one may pass `PETSC_NULL_INTEGER` for `csize`.
6657 
6658   `values` must be declared with
6659 .vb
6660   PetscScalar,dimension(:),pointer   :: values
6661 .ve
6662   and it will be allocated internally by PETSc to hold the values returned
6663 
6664 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6665 @*/
6666 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6667 {
6668   PetscFunctionBeginHot;
6669   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6670   PetscFunctionReturn(PETSC_SUCCESS);
6671 }
6672 
6673 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6674 {
6675   DMLabel            depthLabel;
6676   PetscSection       clSection;
6677   IS                 clPoints;
6678   PetscScalar       *array;
6679   const PetscScalar *vArray;
6680   PetscInt          *points = NULL;
6681   const PetscInt    *clp, *perm = NULL;
6682   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6683 
6684   PetscFunctionBeginHot;
6685   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6686   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6687   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6688   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6689   PetscCall(DMPlexGetDepth(dm, &mdepth));
6690   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6691   PetscCall(PetscSectionGetNumFields(section, &numFields));
6692   if (mdepth == 1 && numFields < 2) {
6693     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6694     PetscFunctionReturn(PETSC_SUCCESS);
6695   }
6696   /* Get points */
6697   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6698   for (clsize = 0, p = 0; p < Np; p++) {
6699     PetscInt dof;
6700     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6701     clsize += dof;
6702   }
6703   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6704   /* Filter points */
6705   for (p = 0; p < numPoints * 2; p += 2) {
6706     PetscInt dep;
6707 
6708     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6709     if (dep != depth) continue;
6710     points[Np * 2 + 0] = points[p];
6711     points[Np * 2 + 1] = points[p + 1];
6712     ++Np;
6713   }
6714   /* Get array */
6715   if (!values || !*values) {
6716     PetscInt asize = 0, dof;
6717 
6718     for (p = 0; p < Np * 2; p += 2) {
6719       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6720       asize += dof;
6721     }
6722     if (!values) {
6723       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6724       if (csize) *csize = asize;
6725       PetscFunctionReturn(PETSC_SUCCESS);
6726     }
6727     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6728   } else {
6729     array = *values;
6730   }
6731   PetscCall(VecGetArrayRead(v, &vArray));
6732   /* Get values */
6733   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6734   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6735   /* Cleanup points */
6736   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6737   /* Cleanup array */
6738   PetscCall(VecRestoreArrayRead(v, &vArray));
6739   if (!*values) {
6740     if (csize) *csize = size;
6741     *values = array;
6742   } else {
6743     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6744     *csize = size;
6745   }
6746   PetscFunctionReturn(PETSC_SUCCESS);
6747 }
6748 
6749 /*@C
6750   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point' obtained with `DMPlexVecGetClosure()`
6751 
6752   Not collective
6753 
6754   Input Parameters:
6755 + dm      - The `DM`
6756 . section - The section describing the layout in `v`, or `NULL` to use the default section
6757 . v       - The local vector
6758 . point   - The point in the `DM`
6759 . csize   - The number of values in the closure, or `NULL`
6760 - values  - The array of values
6761 
6762   Level: intermediate
6763 
6764   Note:
6765   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6766 
6767   Fortran Note:
6768   The `csize` argument is present in the Fortran binding. Since the Fortran `values` array contains its length information this argument may not be needed.
6769   In that case one may pass `PETSC_NULL_INTEGER` for `csize`.
6770 
6771 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6772 @*/
6773 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6774 {
6775   PetscInt size = 0;
6776 
6777   PetscFunctionBegin;
6778   /* Should work without recalculating size */
6779   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6780   *values = NULL;
6781   PetscFunctionReturn(PETSC_SUCCESS);
6782 }
6783 
6784 static inline void add(PetscScalar *x, PetscScalar y)
6785 {
6786   *x += y;
6787 }
6788 static inline void insert(PetscScalar *x, PetscScalar y)
6789 {
6790   *x = y;
6791 }
6792 
6793 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[])
6794 {
6795   PetscInt        cdof;  /* The number of constraints on this point */
6796   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6797   PetscScalar    *a;
6798   PetscInt        off, cind = 0, k;
6799 
6800   PetscFunctionBegin;
6801   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6802   PetscCall(PetscSectionGetOffset(section, point, &off));
6803   a = &array[off];
6804   if (!cdof || setBC) {
6805     if (clperm) {
6806       if (perm) {
6807         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6808       } else {
6809         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6810       }
6811     } else {
6812       if (perm) {
6813         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6814       } else {
6815         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6816       }
6817     }
6818   } else {
6819     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6820     if (clperm) {
6821       if (perm) {
6822         for (k = 0; k < dof; ++k) {
6823           if ((cind < cdof) && (k == cdofs[cind])) {
6824             ++cind;
6825             continue;
6826           }
6827           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6828         }
6829       } else {
6830         for (k = 0; k < dof; ++k) {
6831           if ((cind < cdof) && (k == cdofs[cind])) {
6832             ++cind;
6833             continue;
6834           }
6835           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6836         }
6837       }
6838     } else {
6839       if (perm) {
6840         for (k = 0; k < dof; ++k) {
6841           if ((cind < cdof) && (k == cdofs[cind])) {
6842             ++cind;
6843             continue;
6844           }
6845           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6846         }
6847       } else {
6848         for (k = 0; k < dof; ++k) {
6849           if ((cind < cdof) && (k == cdofs[cind])) {
6850             ++cind;
6851             continue;
6852           }
6853           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6854         }
6855       }
6856     }
6857   }
6858   PetscFunctionReturn(PETSC_SUCCESS);
6859 }
6860 
6861 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[])
6862 {
6863   PetscInt        cdof;  /* The number of constraints on this point */
6864   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6865   PetscScalar    *a;
6866   PetscInt        off, cind = 0, k;
6867 
6868   PetscFunctionBegin;
6869   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6870   PetscCall(PetscSectionGetOffset(section, point, &off));
6871   a = &array[off];
6872   if (cdof) {
6873     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6874     if (clperm) {
6875       if (perm) {
6876         for (k = 0; k < dof; ++k) {
6877           if ((cind < cdof) && (k == cdofs[cind])) {
6878             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6879             cind++;
6880           }
6881         }
6882       } else {
6883         for (k = 0; k < dof; ++k) {
6884           if ((cind < cdof) && (k == cdofs[cind])) {
6885             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6886             cind++;
6887           }
6888         }
6889       }
6890     } else {
6891       if (perm) {
6892         for (k = 0; k < dof; ++k) {
6893           if ((cind < cdof) && (k == cdofs[cind])) {
6894             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6895             cind++;
6896           }
6897         }
6898       } else {
6899         for (k = 0; k < dof; ++k) {
6900           if ((cind < cdof) && (k == cdofs[cind])) {
6901             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6902             cind++;
6903           }
6904         }
6905       }
6906     }
6907   }
6908   PetscFunctionReturn(PETSC_SUCCESS);
6909 }
6910 
6911 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[])
6912 {
6913   PetscScalar    *a;
6914   PetscInt        fdof, foff, fcdof, foffset = *offset;
6915   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6916   PetscInt        cind = 0, b;
6917 
6918   PetscFunctionBegin;
6919   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6920   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6921   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6922   a = &array[foff];
6923   if (!fcdof || setBC) {
6924     if (clperm) {
6925       if (perm) {
6926         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6927       } else {
6928         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6929       }
6930     } else {
6931       if (perm) {
6932         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6933       } else {
6934         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6935       }
6936     }
6937   } else {
6938     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6939     if (clperm) {
6940       if (perm) {
6941         for (b = 0; b < fdof; b++) {
6942           if ((cind < fcdof) && (b == fcdofs[cind])) {
6943             ++cind;
6944             continue;
6945           }
6946           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6947         }
6948       } else {
6949         for (b = 0; b < fdof; b++) {
6950           if ((cind < fcdof) && (b == fcdofs[cind])) {
6951             ++cind;
6952             continue;
6953           }
6954           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6955         }
6956       }
6957     } else {
6958       if (perm) {
6959         for (b = 0; b < fdof; b++) {
6960           if ((cind < fcdof) && (b == fcdofs[cind])) {
6961             ++cind;
6962             continue;
6963           }
6964           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6965         }
6966       } else {
6967         for (b = 0; b < fdof; b++) {
6968           if ((cind < fcdof) && (b == fcdofs[cind])) {
6969             ++cind;
6970             continue;
6971           }
6972           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6973         }
6974       }
6975     }
6976   }
6977   *offset += fdof;
6978   PetscFunctionReturn(PETSC_SUCCESS);
6979 }
6980 
6981 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[])
6982 {
6983   PetscScalar    *a;
6984   PetscInt        fdof, foff, fcdof, foffset = *offset;
6985   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6986   PetscInt        Nc, cind = 0, ncind = 0, b;
6987   PetscBool       ncSet, fcSet;
6988 
6989   PetscFunctionBegin;
6990   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6991   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6992   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6993   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6994   a = &array[foff];
6995   if (fcdof) {
6996     /* We just override fcdof and fcdofs with Ncc and comps */
6997     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6998     if (clperm) {
6999       if (perm) {
7000         if (comps) {
7001           for (b = 0; b < fdof; b++) {
7002             ncSet = fcSet = PETSC_FALSE;
7003             if (b % Nc == comps[ncind]) {
7004               ncind = (ncind + 1) % Ncc;
7005               ncSet = PETSC_TRUE;
7006             }
7007             if ((cind < fcdof) && (b == fcdofs[cind])) {
7008               ++cind;
7009               fcSet = PETSC_TRUE;
7010             }
7011             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
7012           }
7013         } else {
7014           for (b = 0; b < fdof; b++) {
7015             if ((cind < fcdof) && (b == fcdofs[cind])) {
7016               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
7017               ++cind;
7018             }
7019           }
7020         }
7021       } else {
7022         if (comps) {
7023           for (b = 0; b < fdof; b++) {
7024             ncSet = fcSet = PETSC_FALSE;
7025             if (b % Nc == comps[ncind]) {
7026               ncind = (ncind + 1) % Ncc;
7027               ncSet = PETSC_TRUE;
7028             }
7029             if ((cind < fcdof) && (b == fcdofs[cind])) {
7030               ++cind;
7031               fcSet = PETSC_TRUE;
7032             }
7033             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
7034           }
7035         } else {
7036           for (b = 0; b < fdof; b++) {
7037             if ((cind < fcdof) && (b == fcdofs[cind])) {
7038               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
7039               ++cind;
7040             }
7041           }
7042         }
7043       }
7044     } else {
7045       if (perm) {
7046         if (comps) {
7047           for (b = 0; b < fdof; b++) {
7048             ncSet = fcSet = PETSC_FALSE;
7049             if (b % Nc == comps[ncind]) {
7050               ncind = (ncind + 1) % Ncc;
7051               ncSet = PETSC_TRUE;
7052             }
7053             if ((cind < fcdof) && (b == fcdofs[cind])) {
7054               ++cind;
7055               fcSet = PETSC_TRUE;
7056             }
7057             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
7058           }
7059         } else {
7060           for (b = 0; b < fdof; b++) {
7061             if ((cind < fcdof) && (b == fcdofs[cind])) {
7062               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
7063               ++cind;
7064             }
7065           }
7066         }
7067       } else {
7068         if (comps) {
7069           for (b = 0; b < fdof; b++) {
7070             ncSet = fcSet = PETSC_FALSE;
7071             if (b % Nc == comps[ncind]) {
7072               ncind = (ncind + 1) % Ncc;
7073               ncSet = PETSC_TRUE;
7074             }
7075             if ((cind < fcdof) && (b == fcdofs[cind])) {
7076               ++cind;
7077               fcSet = PETSC_TRUE;
7078             }
7079             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
7080           }
7081         } else {
7082           for (b = 0; b < fdof; b++) {
7083             if ((cind < fcdof) && (b == fcdofs[cind])) {
7084               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
7085               ++cind;
7086             }
7087           }
7088         }
7089       }
7090     }
7091   }
7092   *offset += fdof;
7093   PetscFunctionReturn(PETSC_SUCCESS);
7094 }
7095 
7096 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7097 {
7098   PetscScalar    *array;
7099   const PetscInt *cone, *coneO;
7100   PetscInt        pStart, pEnd, p, numPoints, off, dof;
7101 
7102   PetscFunctionBeginHot;
7103   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
7104   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
7105   PetscCall(DMPlexGetCone(dm, point, &cone));
7106   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
7107   PetscCall(VecGetArray(v, &array));
7108   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
7109     const PetscInt cp = !p ? point : cone[p - 1];
7110     const PetscInt o  = !p ? 0 : coneO[p - 1];
7111 
7112     if ((cp < pStart) || (cp >= pEnd)) {
7113       dof = 0;
7114       continue;
7115     }
7116     PetscCall(PetscSectionGetDof(section, cp, &dof));
7117     /* ADD_VALUES */
7118     {
7119       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7120       PetscScalar    *a;
7121       PetscInt        cdof, coff, cind = 0, k;
7122 
7123       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
7124       PetscCall(PetscSectionGetOffset(section, cp, &coff));
7125       a = &array[coff];
7126       if (!cdof) {
7127         if (o >= 0) {
7128           for (k = 0; k < dof; ++k) a[k] += values[off + k];
7129         } else {
7130           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
7131         }
7132       } else {
7133         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
7134         if (o >= 0) {
7135           for (k = 0; k < dof; ++k) {
7136             if ((cind < cdof) && (k == cdofs[cind])) {
7137               ++cind;
7138               continue;
7139             }
7140             a[k] += values[off + k];
7141           }
7142         } else {
7143           for (k = 0; k < dof; ++k) {
7144             if ((cind < cdof) && (k == cdofs[cind])) {
7145               ++cind;
7146               continue;
7147             }
7148             a[k] += values[off + dof - k - 1];
7149           }
7150         }
7151       }
7152     }
7153   }
7154   PetscCall(VecRestoreArray(v, &array));
7155   PetscFunctionReturn(PETSC_SUCCESS);
7156 }
7157 
7158 /*@C
7159   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
7160 
7161   Not collective
7162 
7163   Input Parameters:
7164 + dm      - The `DM`
7165 . section - The section describing the layout in `v`, or `NULL` to use the default section
7166 . v       - The local vector
7167 . point   - The point in the `DM`
7168 . values  - The array of values
7169 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
7170             where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
7171 
7172   Level: intermediate
7173 
7174   Note:
7175   Usually the input arrays were obtained with `DMPlexVecGetClosure()`
7176 
7177 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
7178 @*/
7179 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7180 {
7181   PetscSection    clSection;
7182   IS              clPoints;
7183   PetscScalar    *array;
7184   PetscInt       *points = NULL;
7185   const PetscInt *clp, *clperm = NULL;
7186   PetscInt        depth, numFields, numPoints, p, clsize;
7187 
7188   PetscFunctionBeginHot;
7189   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7190   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7191   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7192   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7193   PetscCall(DMPlexGetDepth(dm, &depth));
7194   PetscCall(PetscSectionGetNumFields(section, &numFields));
7195   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
7196     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
7197     PetscFunctionReturn(PETSC_SUCCESS);
7198   }
7199   /* Get points */
7200   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7201   for (clsize = 0, p = 0; p < numPoints; p++) {
7202     PetscInt dof;
7203     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7204     clsize += dof;
7205   }
7206   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7207   /* Get array */
7208   PetscCall(VecGetArray(v, &array));
7209   /* Get values */
7210   if (numFields > 0) {
7211     PetscInt offset = 0, f;
7212     for (f = 0; f < numFields; ++f) {
7213       const PetscInt    **perms = NULL;
7214       const PetscScalar **flips = NULL;
7215 
7216       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7217       switch (mode) {
7218       case INSERT_VALUES:
7219         for (p = 0; p < numPoints; p++) {
7220           const PetscInt     point = points[2 * p];
7221           const PetscInt    *perm  = perms ? perms[p] : NULL;
7222           const PetscScalar *flip  = flips ? flips[p] : NULL;
7223           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
7224         }
7225         break;
7226       case INSERT_ALL_VALUES:
7227         for (p = 0; p < numPoints; p++) {
7228           const PetscInt     point = points[2 * p];
7229           const PetscInt    *perm  = perms ? perms[p] : NULL;
7230           const PetscScalar *flip  = flips ? flips[p] : NULL;
7231           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
7232         }
7233         break;
7234       case INSERT_BC_VALUES:
7235         for (p = 0; p < numPoints; p++) {
7236           const PetscInt     point = points[2 * p];
7237           const PetscInt    *perm  = perms ? perms[p] : NULL;
7238           const PetscScalar *flip  = flips ? flips[p] : NULL;
7239           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
7240         }
7241         break;
7242       case ADD_VALUES:
7243         for (p = 0; p < numPoints; p++) {
7244           const PetscInt     point = points[2 * p];
7245           const PetscInt    *perm  = perms ? perms[p] : NULL;
7246           const PetscScalar *flip  = flips ? flips[p] : NULL;
7247           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
7248         }
7249         break;
7250       case ADD_ALL_VALUES:
7251         for (p = 0; p < numPoints; p++) {
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(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
7256         }
7257         break;
7258       case ADD_BC_VALUES:
7259         for (p = 0; p < numPoints; p++) {
7260           const PetscInt     point = points[2 * p];
7261           const PetscInt    *perm  = perms ? perms[p] : NULL;
7262           const PetscScalar *flip  = flips ? flips[p] : NULL;
7263           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
7264         }
7265         break;
7266       default:
7267         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7268       }
7269       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7270     }
7271   } else {
7272     PetscInt            dof, off;
7273     const PetscInt    **perms = NULL;
7274     const PetscScalar **flips = NULL;
7275 
7276     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
7277     switch (mode) {
7278     case INSERT_VALUES:
7279       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7280         const PetscInt     point = points[2 * p];
7281         const PetscInt    *perm  = perms ? perms[p] : NULL;
7282         const PetscScalar *flip  = flips ? flips[p] : NULL;
7283         PetscCall(PetscSectionGetDof(section, point, &dof));
7284         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
7285       }
7286       break;
7287     case INSERT_ALL_VALUES:
7288       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7289         const PetscInt     point = points[2 * p];
7290         const PetscInt    *perm  = perms ? perms[p] : NULL;
7291         const PetscScalar *flip  = flips ? flips[p] : NULL;
7292         PetscCall(PetscSectionGetDof(section, point, &dof));
7293         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
7294       }
7295       break;
7296     case INSERT_BC_VALUES:
7297       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7298         const PetscInt     point = points[2 * p];
7299         const PetscInt    *perm  = perms ? perms[p] : NULL;
7300         const PetscScalar *flip  = flips ? flips[p] : NULL;
7301         PetscCall(PetscSectionGetDof(section, point, &dof));
7302         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
7303       }
7304       break;
7305     case ADD_VALUES:
7306       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7307         const PetscInt     point = points[2 * p];
7308         const PetscInt    *perm  = perms ? perms[p] : NULL;
7309         const PetscScalar *flip  = flips ? flips[p] : NULL;
7310         PetscCall(PetscSectionGetDof(section, point, &dof));
7311         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
7312       }
7313       break;
7314     case ADD_ALL_VALUES:
7315       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7316         const PetscInt     point = points[2 * p];
7317         const PetscInt    *perm  = perms ? perms[p] : NULL;
7318         const PetscScalar *flip  = flips ? flips[p] : NULL;
7319         PetscCall(PetscSectionGetDof(section, point, &dof));
7320         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
7321       }
7322       break;
7323     case ADD_BC_VALUES:
7324       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7325         const PetscInt     point = points[2 * p];
7326         const PetscInt    *perm  = perms ? perms[p] : NULL;
7327         const PetscScalar *flip  = flips ? flips[p] : NULL;
7328         PetscCall(PetscSectionGetDof(section, point, &dof));
7329         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
7330       }
7331       break;
7332     default:
7333       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7334     }
7335     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
7336   }
7337   /* Cleanup points */
7338   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7339   /* Cleanup array */
7340   PetscCall(VecRestoreArray(v, &array));
7341   PetscFunctionReturn(PETSC_SUCCESS);
7342 }
7343 
7344 /* Check whether the given point is in the label. If not, update the offset to skip this point */
7345 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
7346 {
7347   PetscFunctionBegin;
7348   *contains = PETSC_TRUE;
7349   if (label) {
7350     PetscInt fdof;
7351 
7352     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
7353     if (!*contains) {
7354       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7355       *offset += fdof;
7356       PetscFunctionReturn(PETSC_SUCCESS);
7357     }
7358   }
7359   PetscFunctionReturn(PETSC_SUCCESS);
7360 }
7361 
7362 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
7363 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)
7364 {
7365   PetscSection    clSection;
7366   IS              clPoints;
7367   PetscScalar    *array;
7368   PetscInt       *points = NULL;
7369   const PetscInt *clp;
7370   PetscInt        numFields, numPoints, p;
7371   PetscInt        offset = 0, f;
7372 
7373   PetscFunctionBeginHot;
7374   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7375   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7376   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7377   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7378   PetscCall(PetscSectionGetNumFields(section, &numFields));
7379   /* Get points */
7380   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7381   /* Get array */
7382   PetscCall(VecGetArray(v, &array));
7383   /* Get values */
7384   for (f = 0; f < numFields; ++f) {
7385     const PetscInt    **perms = NULL;
7386     const PetscScalar **flips = NULL;
7387     PetscBool           contains;
7388 
7389     if (!fieldActive[f]) {
7390       for (p = 0; p < numPoints * 2; p += 2) {
7391         PetscInt fdof;
7392         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7393         offset += fdof;
7394       }
7395       continue;
7396     }
7397     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7398     switch (mode) {
7399     case INSERT_VALUES:
7400       for (p = 0; p < numPoints; p++) {
7401         const PetscInt     point = points[2 * p];
7402         const PetscInt    *perm  = perms ? perms[p] : NULL;
7403         const PetscScalar *flip  = flips ? flips[p] : NULL;
7404         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7405         if (!contains) continue;
7406         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
7407       }
7408       break;
7409     case INSERT_ALL_VALUES:
7410       for (p = 0; p < numPoints; p++) {
7411         const PetscInt     point = points[2 * p];
7412         const PetscInt    *perm  = perms ? perms[p] : NULL;
7413         const PetscScalar *flip  = flips ? flips[p] : NULL;
7414         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7415         if (!contains) continue;
7416         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
7417       }
7418       break;
7419     case INSERT_BC_VALUES:
7420       for (p = 0; p < numPoints; p++) {
7421         const PetscInt     point = points[2 * p];
7422         const PetscInt    *perm  = perms ? perms[p] : NULL;
7423         const PetscScalar *flip  = flips ? flips[p] : NULL;
7424         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7425         if (!contains) continue;
7426         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
7427       }
7428       break;
7429     case ADD_VALUES:
7430       for (p = 0; p < numPoints; p++) {
7431         const PetscInt     point = points[2 * p];
7432         const PetscInt    *perm  = perms ? perms[p] : NULL;
7433         const PetscScalar *flip  = flips ? flips[p] : NULL;
7434         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7435         if (!contains) continue;
7436         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
7437       }
7438       break;
7439     case ADD_ALL_VALUES:
7440       for (p = 0; p < numPoints; p++) {
7441         const PetscInt     point = points[2 * p];
7442         const PetscInt    *perm  = perms ? perms[p] : NULL;
7443         const PetscScalar *flip  = flips ? flips[p] : NULL;
7444         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7445         if (!contains) continue;
7446         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
7447       }
7448       break;
7449     default:
7450       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7451     }
7452     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7453   }
7454   /* Cleanup points */
7455   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7456   /* Cleanup array */
7457   PetscCall(VecRestoreArray(v, &array));
7458   PetscFunctionReturn(PETSC_SUCCESS);
7459 }
7460 
7461 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
7462 {
7463   PetscMPIInt rank;
7464   PetscInt    i, j;
7465 
7466   PetscFunctionBegin;
7467   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7468   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
7469   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
7470   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
7471   numCIndices = numCIndices ? numCIndices : numRIndices;
7472   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
7473   for (i = 0; i < numRIndices; i++) {
7474     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
7475     for (j = 0; j < numCIndices; j++) {
7476 #if defined(PETSC_USE_COMPLEX)
7477       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
7478 #else
7479       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
7480 #endif
7481     }
7482     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
7483   }
7484   PetscFunctionReturn(PETSC_SUCCESS);
7485 }
7486 
7487 /*
7488   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
7489 
7490   Input Parameters:
7491 + section - The section for this data layout
7492 . islocal - Is the section (and thus indices being requested) local or global?
7493 . point   - The point contributing dofs with these indices
7494 . off     - The global offset of this point
7495 . loff    - The local offset of each field
7496 . setBC   - The flag determining whether to include indices of boundary values
7497 . perm    - A permutation of the dofs on this point, or NULL
7498 - indperm - A permutation of the entire indices array, or NULL
7499 
7500   Output Parameter:
7501 . indices - Indices for dofs on this point
7502 
7503   Level: developer
7504 
7505   Note: The indices could be local or global, depending on the value of 'off'.
7506 */
7507 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7508 {
7509   PetscInt        dof;   /* The number of unknowns on this point */
7510   PetscInt        cdof;  /* The number of constraints on this point */
7511   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7512   PetscInt        cind = 0, k;
7513 
7514   PetscFunctionBegin;
7515   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7516   PetscCall(PetscSectionGetDof(section, point, &dof));
7517   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7518   if (!cdof || setBC) {
7519     for (k = 0; k < dof; ++k) {
7520       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7521       const PetscInt ind    = indperm ? indperm[preind] : preind;
7522 
7523       indices[ind] = off + k;
7524     }
7525   } else {
7526     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7527     for (k = 0; k < dof; ++k) {
7528       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7529       const PetscInt ind    = indperm ? indperm[preind] : preind;
7530 
7531       if ((cind < cdof) && (k == cdofs[cind])) {
7532         /* Insert check for returning constrained indices */
7533         indices[ind] = -(off + k + 1);
7534         ++cind;
7535       } else {
7536         indices[ind] = off + k - (islocal ? 0 : cind);
7537       }
7538     }
7539   }
7540   *loff += dof;
7541   PetscFunctionReturn(PETSC_SUCCESS);
7542 }
7543 
7544 /*
7545  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7546 
7547  Input Parameters:
7548 + section - a section (global or local)
7549 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7550 . point - point within section
7551 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7552 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7553 . setBC - identify constrained (boundary condition) points via involution.
7554 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7555 . permsoff - offset
7556 - indperm - index permutation
7557 
7558  Output Parameter:
7559 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7560 . indices - array to hold indices (as defined by section) of each dof associated with point
7561 
7562  Notes:
7563  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7564  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7565  in the local vector.
7566 
7567  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7568  significant).  It is invalid to call with a global section and setBC=true.
7569 
7570  Developer Note:
7571  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7572  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7573  offset could be obtained from the section instead of passing it explicitly as we do now.
7574 
7575  Example:
7576  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7577  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7578  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7579  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.
7580 
7581  Level: developer
7582 */
7583 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[])
7584 {
7585   PetscInt numFields, foff, f;
7586 
7587   PetscFunctionBegin;
7588   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7589   PetscCall(PetscSectionGetNumFields(section, &numFields));
7590   for (f = 0, foff = 0; f < numFields; ++f) {
7591     PetscInt        fdof, cfdof;
7592     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7593     PetscInt        cind = 0, b;
7594     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7595 
7596     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7597     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7598     if (!cfdof || setBC) {
7599       for (b = 0; b < fdof; ++b) {
7600         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7601         const PetscInt ind    = indperm ? indperm[preind] : preind;
7602 
7603         indices[ind] = off + foff + b;
7604       }
7605     } else {
7606       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7607       for (b = 0; b < fdof; ++b) {
7608         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7609         const PetscInt ind    = indperm ? indperm[preind] : preind;
7610 
7611         if ((cind < cfdof) && (b == fcdofs[cind])) {
7612           indices[ind] = -(off + foff + b + 1);
7613           ++cind;
7614         } else {
7615           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7616         }
7617       }
7618     }
7619     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7620     foffs[f] += fdof;
7621   }
7622   PetscFunctionReturn(PETSC_SUCCESS);
7623 }
7624 
7625 /*
7626   This version believes the globalSection offsets for each field, rather than just the point offset
7627 
7628  . foffs - The offset into 'indices' for each field, since it is segregated by field
7629 
7630  Notes:
7631  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7632  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7633 */
7634 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7635 {
7636   PetscInt numFields, foff, f;
7637 
7638   PetscFunctionBegin;
7639   PetscCall(PetscSectionGetNumFields(section, &numFields));
7640   for (f = 0; f < numFields; ++f) {
7641     PetscInt        fdof, cfdof;
7642     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7643     PetscInt        cind = 0, b;
7644     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7645 
7646     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7647     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7648     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7649     if (!cfdof) {
7650       for (b = 0; b < fdof; ++b) {
7651         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7652         const PetscInt ind    = indperm ? indperm[preind] : preind;
7653 
7654         indices[ind] = foff + b;
7655       }
7656     } else {
7657       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7658       for (b = 0; b < fdof; ++b) {
7659         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7660         const PetscInt ind    = indperm ? indperm[preind] : preind;
7661 
7662         if ((cind < cfdof) && (b == fcdofs[cind])) {
7663           indices[ind] = -(foff + b + 1);
7664           ++cind;
7665         } else {
7666           indices[ind] = foff + b - cind;
7667         }
7668       }
7669     }
7670     foffs[f] += fdof;
7671   }
7672   PetscFunctionReturn(PETSC_SUCCESS);
7673 }
7674 
7675 static PetscErrorCode DMPlexAnchorsGetSubMatIndices(PetscInt nPoints, const PetscInt pnts[], PetscSection section, PetscSection cSec, PetscInt tmpIndices[], PetscInt fieldOffsets[], PetscInt indices[], const PetscInt ***perms)
7676 {
7677   PetscInt numFields, sStart, sEnd, cStart, cEnd;
7678 
7679   PetscFunctionBegin;
7680   PetscCall(PetscSectionGetNumFields(section, &numFields));
7681   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7682   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7683   for (PetscInt p = 0; p < nPoints; p++) {
7684     PetscInt     b       = pnts[2 * p];
7685     PetscInt     bSecDof = 0, bOff;
7686     PetscInt     cSecDof = 0;
7687     PetscSection indices_section;
7688 
7689     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7690     if (!bSecDof) continue;
7691     if (b >= cStart && b < cEnd) PetscCall(PetscSectionGetDof(cSec, b, &cSecDof));
7692     indices_section = cSecDof > 0 ? cSec : section;
7693     if (numFields) {
7694       PetscInt fStart[32], fEnd[32];
7695 
7696       fStart[0] = 0;
7697       fEnd[0]   = 0;
7698       for (PetscInt f = 0; f < numFields; f++) {
7699         PetscInt fDof = 0;
7700 
7701         PetscCall(PetscSectionGetFieldDof(indices_section, b, f, &fDof));
7702         fStart[f + 1] = fStart[f] + fDof;
7703         fEnd[f + 1]   = fStart[f + 1];
7704       }
7705       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7706       // only apply permutations on one side
7707       PetscCall(DMPlexGetIndicesPointFields_Internal(indices_section, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, perms ? p : -1, NULL, tmpIndices));
7708       for (PetscInt f = 0; f < numFields; f++) {
7709         for (PetscInt i = fStart[f]; i < fEnd[f]; i++) { indices[fieldOffsets[f]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1); }
7710       }
7711     } else {
7712       PetscInt bEnd = 0;
7713 
7714       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7715       PetscCall(DMPlexGetIndicesPoint_Internal(indices_section, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, tmpIndices));
7716 
7717       for (PetscInt i = 0; i < bEnd; i++) indices[fieldOffsets[0]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1);
7718     }
7719   }
7720   PetscFunctionReturn(PETSC_SUCCESS);
7721 }
7722 
7723 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[])
7724 {
7725   Mat             cMat;
7726   PetscSection    aSec, cSec;
7727   IS              aIS;
7728   PetscInt        aStart = -1, aEnd = -1;
7729   PetscInt        sStart = -1, sEnd = -1;
7730   PetscInt        cStart = -1, cEnd = -1;
7731   const PetscInt *anchors;
7732   PetscInt        numFields, p;
7733   PetscInt        newNumPoints = 0, newNumIndices = 0;
7734   PetscInt       *newPoints, *indices, *newIndices, *tmpIndices, *tmpNewIndices;
7735   PetscInt        oldOffsets[32];
7736   PetscInt        newOffsets[32];
7737   PetscInt        oldOffsetsCopy[32];
7738   PetscInt        newOffsetsCopy[32];
7739   PetscScalar    *modMat         = NULL;
7740   PetscBool       anyConstrained = PETSC_FALSE;
7741 
7742   PetscFunctionBegin;
7743   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7744   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7745   PetscCall(PetscSectionGetNumFields(section, &numFields));
7746 
7747   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7748   /* if there are point-to-point constraints */
7749   if (aSec) {
7750     PetscCall(PetscArrayzero(newOffsets, 32));
7751     PetscCall(PetscArrayzero(oldOffsets, 32));
7752     PetscCall(ISGetIndices(aIS, &anchors));
7753     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7754     PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7755     /* figure out how many points are going to be in the new element matrix
7756      * (we allow double counting, because it's all just going to be summed
7757      * into the global matrix anyway) */
7758     for (p = 0; p < 2 * numPoints; p += 2) {
7759       PetscInt b    = points[p];
7760       PetscInt bDof = 0, bSecDof = 0;
7761 
7762       if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7763       if (!bSecDof) continue;
7764 
7765       for (PetscInt f = 0; f < numFields; f++) {
7766         PetscInt fDof = 0;
7767 
7768         PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7769         oldOffsets[f + 1] += fDof;
7770       }
7771       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7772       if (bDof) {
7773         /* this point is constrained */
7774         /* it is going to be replaced by its anchors */
7775         PetscInt bOff, q;
7776 
7777         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7778         for (q = 0; q < bDof; q++) {
7779           PetscInt a    = anchors[bOff + q];
7780           PetscInt aDof = 0;
7781 
7782           if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7783           if (aDof) {
7784             anyConstrained = PETSC_TRUE;
7785             newNumPoints += 1;
7786           }
7787           newNumIndices += aDof;
7788           for (PetscInt f = 0; f < numFields; ++f) {
7789             PetscInt fDof = 0;
7790 
7791             if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7792             newOffsets[f + 1] += fDof;
7793           }
7794         }
7795       } else {
7796         /* this point is not constrained */
7797         newNumPoints++;
7798         newNumIndices += bSecDof;
7799         for (PetscInt f = 0; f < numFields; ++f) {
7800           PetscInt fDof;
7801 
7802           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7803           newOffsets[f + 1] += fDof;
7804         }
7805       }
7806     }
7807   }
7808   if (!anyConstrained) {
7809     if (outNumPoints) *outNumPoints = 0;
7810     if (outNumIndices) *outNumIndices = 0;
7811     if (outPoints) *outPoints = NULL;
7812     if (outMat) *outMat = NULL;
7813     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7814     PetscFunctionReturn(PETSC_SUCCESS);
7815   }
7816 
7817   if (outNumPoints) *outNumPoints = newNumPoints;
7818   if (outNumIndices) *outNumIndices = newNumIndices;
7819 
7820   for (PetscInt f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7821   for (PetscInt f = 0; f < numFields; ++f) oldOffsets[f + 1] += oldOffsets[f];
7822 
7823   if (!outPoints && !outMat) {
7824     if (offsets) {
7825       for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7826     }
7827     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7828     PetscFunctionReturn(PETSC_SUCCESS);
7829   }
7830 
7831   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7832   PetscCheck(!numFields || oldOffsets[numFields] == numIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, oldOffsets[numFields], numIndices);
7833 
7834   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7835   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7836 
7837   /* output arrays */
7838   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7839   PetscCall(PetscArrayzero(newPoints, 2 * newNumPoints));
7840 
7841   // get the new Points
7842   for (PetscInt p = 0, newP = 0; p < numPoints; p++) {
7843     PetscInt b    = points[2 * p];
7844     PetscInt bDof = 0, bSecDof = 0, bOff;
7845 
7846     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7847     if (!bSecDof) continue;
7848     if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7849     if (bDof) {
7850       PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7851       for (PetscInt q = 0; q < bDof; q++) {
7852         PetscInt a = anchors[bOff + q], aDof = 0;
7853 
7854         if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7855         if (aDof) {
7856           newPoints[2 * newP]     = a;
7857           newPoints[2 * newP + 1] = 0; // orientations are accounted for in constructing the matrix, newly added points are in default orientation
7858           newP++;
7859         }
7860       }
7861     } else {
7862       newPoints[2 * newP]     = b;
7863       newPoints[2 * newP + 1] = points[2 * p + 1];
7864       newP++;
7865     }
7866   }
7867 
7868   if (outMat) {
7869     PetscScalar *tmpMat;
7870     PetscCall(PetscArraycpy(oldOffsetsCopy, oldOffsets, 32));
7871     PetscCall(PetscArraycpy(newOffsetsCopy, newOffsets, 32));
7872 
7873     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &indices));
7874     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7875     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7876     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7877 
7878     for (PetscInt i = 0; i < numIndices; i++) indices[i] = -1;
7879     for (PetscInt i = 0; i < newNumIndices; i++) newIndices[i] = -1;
7880 
7881     PetscCall(DMPlexAnchorsGetSubMatIndices(numPoints, points, section, cSec, tmpIndices, oldOffsetsCopy, indices, perms));
7882     PetscCall(DMPlexAnchorsGetSubMatIndices(newNumPoints, newPoints, section, section, tmpNewIndices, newOffsetsCopy, newIndices, NULL));
7883 
7884     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7885     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7886     PetscCall(PetscArrayzero(modMat, newNumIndices * numIndices));
7887     // for each field, insert the anchor modification into modMat
7888     for (PetscInt f = 0; f < PetscMax(1, numFields); f++) {
7889       PetscInt fStart    = oldOffsets[f];
7890       PetscInt fNewStart = newOffsets[f];
7891       for (PetscInt p = 0, newP = 0, o = fStart, oNew = fNewStart; p < numPoints; p++) {
7892         PetscInt b    = points[2 * p];
7893         PetscInt bDof = 0, bSecDof = 0, bOff;
7894 
7895         if (b >= sStart && b < sEnd) {
7896           if (numFields) {
7897             PetscCall(PetscSectionGetFieldDof(section, b, f, &bSecDof));
7898           } else {
7899             PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7900           }
7901         }
7902         if (!bSecDof) continue;
7903         if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7904         if (bDof) {
7905           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7906           for (PetscInt q = 0; q < bDof; q++, newP++) {
7907             PetscInt a = anchors[bOff + q], aDof = 0;
7908 
7909             if (a >= sStart && a < sEnd) {
7910               if (numFields) {
7911                 PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
7912               } else {
7913                 PetscCall(PetscSectionGetDof(section, a, &aDof));
7914               }
7915             }
7916             if (aDof) {
7917               PetscCall(MatGetValues(cMat, bSecDof, &indices[o], aDof, &newIndices[oNew], tmpMat));
7918               for (PetscInt d = 0; d < bSecDof; d++) {
7919                 for (PetscInt e = 0; e < aDof; e++) modMat[(o + d) * newNumIndices + oNew + e] = tmpMat[d * aDof + e];
7920               }
7921             }
7922             oNew += aDof;
7923           }
7924         } else {
7925           // Insert the identity matrix in this block
7926           for (PetscInt d = 0; d < bSecDof; d++) modMat[(o + d) * newNumIndices + oNew + d] = 1;
7927           oNew += bSecDof;
7928           newP++;
7929         }
7930         o += bSecDof;
7931       }
7932     }
7933 
7934     *outMat = modMat;
7935 
7936     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7937     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7938     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7939     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7940     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &indices));
7941   }
7942   PetscCall(ISRestoreIndices(aIS, &anchors));
7943 
7944   /* output */
7945   if (outPoints) {
7946     *outPoints = newPoints;
7947   } else {
7948     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7949   }
7950   for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7951   PetscFunctionReturn(PETSC_SUCCESS);
7952 }
7953 
7954 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)
7955 {
7956   PetscScalar *modMat        = NULL;
7957   PetscInt     newNumIndices = -1;
7958 
7959   PetscFunctionBegin;
7960   /* 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.
7961      modMat is that matrix C */
7962   PetscCall(DMPlexAnchorsGetSubMatModification(dm, section, numPoints, numIndices, points, perms, outNumPoints, &newNumIndices, outPoints, offsets, outValues ? &modMat : NULL));
7963   if (outNumIndices) *outNumIndices = newNumIndices;
7964   if (modMat) {
7965     const PetscScalar *newValues = values;
7966 
7967     if (multiplyRight) {
7968       PetscScalar *newNewValues = NULL;
7969       PetscBLASInt M, N, K;
7970       PetscScalar  a = 1.0, b = 0.0;
7971 
7972       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);
7973 
7974       PetscCall(PetscBLASIntCast(newNumIndices, &M));
7975       PetscCall(PetscBLASIntCast(numRows, &N));
7976       PetscCall(PetscBLASIntCast(numIndices, &K));
7977       PetscCall(DMGetWorkArray(dm, numRows * newNumIndices, MPIU_SCALAR, &newNewValues));
7978       // row-major to column-major conversion, right multiplication becomes left multiplication
7979       PetscCallBLAS("BLASgemm", BLASgemm_("N", "N", &M, &N, &K, &a, modMat, &M, newValues, &K, &b, newNewValues, &M));
7980       numCols   = newNumIndices;
7981       newValues = newNewValues;
7982     }
7983 
7984     if (multiplyLeft) {
7985       PetscScalar *newNewValues = NULL;
7986       PetscBLASInt M, N, K;
7987       PetscScalar  a = 1.0, b = 0.0;
7988 
7989       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);
7990 
7991       PetscCall(PetscBLASIntCast(numCols, &M));
7992       PetscCall(PetscBLASIntCast(newNumIndices, &N));
7993       PetscCall(PetscBLASIntCast(numIndices, &K));
7994       PetscCall(DMGetWorkArray(dm, newNumIndices * numCols, MPIU_SCALAR, &newNewValues));
7995       // row-major to column-major conversion, left multiplication becomes right multiplication
7996       PetscCallBLAS("BLASgemm", BLASgemm_("N", "T", &M, &N, &K, &a, newValues, &M, modMat, &N, &b, newNewValues, &M));
7997       if (newValues != values) PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &newValues));
7998       newValues = newNewValues;
7999     }
8000     *outValues = (PetscScalar *)newValues;
8001     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
8002   }
8003   PetscFunctionReturn(PETSC_SUCCESS);
8004 }
8005 
8006 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)
8007 {
8008   PetscFunctionBegin;
8009   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, numPoints, numIndices, points, perms, numIndices, numIndices, values, outNumPoints, outNumIndices, outPoints, outValues, offsets, PETSC_TRUE, multiplyLeft));
8010   PetscFunctionReturn(PETSC_SUCCESS);
8011 }
8012 
8013 static PetscErrorCode DMPlexGetClosureIndicesSize_Internal(DM dm, PetscSection section, PetscInt point, PetscInt *closureSize)
8014 {
8015   /* Closure ordering */
8016   PetscSection    clSection;
8017   IS              clPoints;
8018   const PetscInt *clp;
8019   PetscInt       *points;
8020   PetscInt        Ncl, Ni = 0;
8021 
8022   PetscFunctionBeginHot;
8023   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
8024   for (PetscInt p = 0; p < Ncl * 2; p += 2) {
8025     PetscInt dof;
8026 
8027     PetscCall(PetscSectionGetDof(section, points[p], &dof));
8028     Ni += dof;
8029   }
8030   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8031   *closureSize = Ni;
8032   PetscFunctionReturn(PETSC_SUCCESS);
8033 }
8034 
8035 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)
8036 {
8037   /* Closure ordering */
8038   PetscSection    clSection;
8039   IS              clPoints;
8040   const PetscInt *clp;
8041   PetscInt       *points;
8042   const PetscInt *clperm = NULL;
8043   /* Dof permutation and sign flips */
8044   const PetscInt    **perms[32] = {NULL};
8045   const PetscScalar **flips[32] = {NULL};
8046   PetscScalar        *valCopy   = NULL;
8047   /* Hanging node constraints */
8048   PetscInt    *pointsC = NULL;
8049   PetscScalar *valuesC = NULL;
8050   PetscInt     NclC, NiC;
8051 
8052   PetscInt *idx;
8053   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
8054   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
8055   PetscInt  idxStart, idxEnd;
8056   PetscInt  nRows, nCols;
8057 
8058   PetscFunctionBeginHot;
8059   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8060   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8061   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
8062   PetscAssertPointer(numRows, 6);
8063   PetscAssertPointer(numCols, 7);
8064   if (indices) PetscAssertPointer(indices, 8);
8065   if (outOffsets) PetscAssertPointer(outOffsets, 9);
8066   if (values) PetscAssertPointer(values, 10);
8067   PetscCall(PetscSectionGetNumFields(section, &Nf));
8068   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
8069   PetscCall(PetscArrayzero(offsets, 32));
8070   /* 1) Get points in closure */
8071   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
8072   if (useClPerm) {
8073     PetscInt depth, clsize;
8074     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
8075     for (clsize = 0, p = 0; p < Ncl; p++) {
8076       PetscInt dof;
8077       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
8078       clsize += dof;
8079     }
8080     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
8081   }
8082   /* 2) Get number of indices on these points and field offsets from section */
8083   for (p = 0; p < Ncl * 2; p += 2) {
8084     PetscInt dof, fdof;
8085 
8086     PetscCall(PetscSectionGetDof(section, points[p], &dof));
8087     for (f = 0; f < Nf; ++f) {
8088       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
8089       offsets[f + 1] += fdof;
8090     }
8091     Ni += dof;
8092   }
8093   if (*numRows == -1) *numRows = Ni;
8094   if (*numCols == -1) *numCols = Ni;
8095   nRows = *numRows;
8096   nCols = *numCols;
8097   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
8098   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
8099   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
8100   if (multiplyRight) PetscCheck(nCols == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " columns, got %" PetscInt_FMT, Ni, nCols);
8101   if (multiplyLeft) PetscCheck(nRows == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " rows, got %" PetscInt_FMT, Ni, nRows);
8102   for (f = 0; f < PetscMax(1, Nf); ++f) {
8103     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8104     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
8105     /* may need to apply sign changes to the element matrix */
8106     if (values && flips[f]) {
8107       PetscInt foffset = offsets[f];
8108 
8109       for (p = 0; p < Ncl; ++p) {
8110         PetscInt           pnt  = points[2 * p], fdof;
8111         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
8112 
8113         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
8114         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
8115         if (flip) {
8116           PetscInt i, j, k;
8117 
8118           if (!valCopy) {
8119             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8120             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
8121             *values = valCopy;
8122           }
8123           for (i = 0; i < fdof; ++i) {
8124             PetscScalar fval = flip[i];
8125 
8126             if (multiplyRight) {
8127               for (k = 0; k < nRows; ++k) { valCopy[Ni * k + (foffset + i)] *= fval; }
8128             }
8129             if (multiplyLeft) {
8130               for (k = 0; k < nCols; ++k) { valCopy[nCols * (foffset + i) + k] *= fval; }
8131             }
8132           }
8133         }
8134         foffset += fdof;
8135       }
8136     }
8137   }
8138   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
8139   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, Ncl, Ni, points, perms, nRows, nCols, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, multiplyRight, multiplyLeft));
8140   if (NclC) {
8141     if (multiplyRight) *numCols = NiC;
8142     if (multiplyLeft) *numRows = NiC;
8143     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8144     for (f = 0; f < PetscMax(1, Nf); ++f) {
8145       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8146       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8147     }
8148     for (f = 0; f < PetscMax(1, Nf); ++f) {
8149       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
8150       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
8151     }
8152     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8153     Ncl    = NclC;
8154     Ni     = NiC;
8155     points = pointsC;
8156     if (values) *values = valuesC;
8157   }
8158   /* 5) Calculate indices */
8159   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
8160   PetscCall(PetscSectionGetChart(idxSection, &idxStart, &idxEnd));
8161   if (Nf) {
8162     PetscInt  idxOff;
8163     PetscBool useFieldOffsets;
8164 
8165     if (outOffsets) {
8166       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
8167     }
8168     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
8169     if (useFieldOffsets) {
8170       for (p = 0; p < Ncl; ++p) {
8171         const PetscInt pnt = points[p * 2];
8172 
8173         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
8174       }
8175     } else {
8176       for (p = 0; p < Ncl; ++p) {
8177         const PetscInt pnt = points[p * 2];
8178 
8179         if (pnt < idxStart || pnt >= idxEnd) continue;
8180         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8181         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8182          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
8183          * global section. */
8184         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
8185       }
8186     }
8187   } else {
8188     PetscInt off = 0, idxOff;
8189 
8190     for (p = 0; p < Ncl; ++p) {
8191       const PetscInt  pnt  = points[p * 2];
8192       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
8193 
8194       if (pnt < idxStart || pnt >= idxEnd) continue;
8195       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8196       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8197        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
8198       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
8199     }
8200   }
8201   /* 6) Cleanup */
8202   for (f = 0; f < PetscMax(1, Nf); ++f) {
8203     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8204     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8205   }
8206   if (NclC) {
8207     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
8208   } else {
8209     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8210   }
8211 
8212   if (indices) *indices = idx;
8213   PetscFunctionReturn(PETSC_SUCCESS);
8214 }
8215 
8216 /*@C
8217   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
8218 
8219   Not collective
8220 
8221   Input Parameters:
8222 + dm         - The `DM`
8223 . section    - The `PetscSection` describing the points (a local section)
8224 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8225 . point      - The point defining the closure
8226 - useClPerm  - Use the closure point permutation if available
8227 
8228   Output Parameters:
8229 + numIndices - The number of dof indices in the closure of point with the input sections
8230 . indices    - The dof indices
8231 . outOffsets - Array to write the field offsets into, or `NULL`
8232 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8233 
8234   Level: advanced
8235 
8236   Notes:
8237   Call `DMPlexRestoreClosureIndices()` to free allocated memory
8238 
8239   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8240   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
8241   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8242   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
8243   indices (with the above semantics) are implied.
8244 
8245 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
8246           `PetscSection`, `DMGetGlobalSection()`
8247 @*/
8248 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PeOp PetscInt outOffsets[], PeOp PetscScalar *values[])
8249 {
8250   PetscInt numRows = -1, numCols = -1;
8251 
8252   PetscFunctionBeginHot;
8253   PetscCall(DMPlexGetClosureIndices_Internal(dm, section, idxSection, point, useClPerm, &numRows, &numCols, indices, outOffsets, values, PETSC_TRUE, PETSC_TRUE));
8254   PetscCheck(numRows == numCols, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Symmetric matrix transformation produces rectangular dimensions (%" PetscInt_FMT ", %" PetscInt_FMT ")", numRows, numCols);
8255   *numIndices = numRows;
8256   PetscFunctionReturn(PETSC_SUCCESS);
8257 }
8258 
8259 /*@C
8260   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
8261 
8262   Not collective
8263 
8264   Input Parameters:
8265 + dm         - The `DM`
8266 . section    - The `PetscSection` describing the points (a local section)
8267 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8268 . point      - The point defining the closure
8269 - useClPerm  - Use the closure point permutation if available
8270 
8271   Output Parameters:
8272 + numIndices - The number of dof indices in the closure of point with the input sections
8273 . indices    - The dof indices
8274 . outOffsets - Array to write the field offsets into, or `NULL`
8275 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8276 
8277   Level: advanced
8278 
8279   Notes:
8280   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
8281 
8282   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8283   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
8284   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8285   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
8286   indices (with the above semantics) are implied.
8287 
8288 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
8289 @*/
8290 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PeOp PetscInt outOffsets[], PeOp PetscScalar *values[])
8291 {
8292   PetscFunctionBegin;
8293   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8294   PetscAssertPointer(indices, 7);
8295   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
8296   PetscFunctionReturn(PETSC_SUCCESS);
8297 }
8298 
8299 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8300 {
8301   DM_Plex           *mesh = (DM_Plex *)dm->data;
8302   PetscInt          *indices;
8303   PetscInt           numIndices;
8304   const PetscScalar *valuesOrig = values;
8305   PetscErrorCode     ierr;
8306 
8307   PetscFunctionBegin;
8308   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8309   if (!section) PetscCall(DMGetLocalSection(dm, &section));
8310   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8311   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
8312   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
8313   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
8314 
8315   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
8316 
8317   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
8318   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8319   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
8320   if (ierr) {
8321     PetscMPIInt rank;
8322 
8323     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8324     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8325     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
8326     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8327     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8328     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
8329   }
8330   if (mesh->printFEM > 1) {
8331     PetscInt i;
8332     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
8333     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
8334     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8335   }
8336 
8337   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8338   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8339   PetscFunctionReturn(PETSC_SUCCESS);
8340 }
8341 
8342 /*@C
8343   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
8344 
8345   Not collective
8346 
8347   Input Parameters:
8348 + dm            - The `DM`
8349 . section       - The section describing the layout in `v`, or `NULL` to use the default section
8350 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
8351 . A             - The matrix
8352 . point         - The point in the `DM`
8353 . values        - The array of values
8354 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8355 
8356   Level: intermediate
8357 
8358 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8359 @*/
8360 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8361 {
8362   PetscFunctionBegin;
8363   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
8364   PetscFunctionReturn(PETSC_SUCCESS);
8365 }
8366 
8367 /*@C
8368   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
8369 
8370   Not collective
8371 
8372   Input Parameters:
8373 + dmRow            - The `DM` for the row fields
8374 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
8375 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
8376 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
8377 . dmCol            - The `DM` for the column fields
8378 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
8379 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
8380 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
8381 . A                - The matrix
8382 . point            - The point in the `DM`
8383 . values           - The array of values
8384 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8385 
8386   Level: intermediate
8387 
8388 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8389 @*/
8390 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)
8391 {
8392   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
8393   PetscInt          *indicesRow, *indicesCol;
8394   PetscInt           numIndicesRow = -1, numIndicesCol = -1;
8395   const PetscScalar *valuesV0 = values, *valuesV1, *valuesV2;
8396 
8397   PetscErrorCode ierr;
8398 
8399   PetscFunctionBegin;
8400   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
8401   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
8402   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
8403   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
8404   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
8405   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
8406   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
8407   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
8408   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
8409   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
8410   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
8411 
8412   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmRow, sectionRow, point, &numIndicesRow));
8413   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmCol, sectionCol, point, &numIndicesCol));
8414   valuesV1 = valuesV0;
8415   PetscCall(DMPlexGetClosureIndices_Internal(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV1, PETSC_FALSE, PETSC_TRUE));
8416   valuesV2 = valuesV1;
8417   PetscCall(DMPlexGetClosureIndices_Internal(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesRow, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2, PETSC_TRUE, PETSC_FALSE));
8418 
8419   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2));
8420   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8421   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2, mode);
8422   if (ierr) {
8423     PetscMPIInt rank;
8424 
8425     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8426     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8427     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8428     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV2));
8429     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8430     if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8431     if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8432   }
8433 
8434   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2));
8435   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8436   if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8437   if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8438   PetscFunctionReturn(PETSC_SUCCESS);
8439 }
8440 
8441 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8442 {
8443   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8444   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8445   PetscInt       *cpoints = NULL;
8446   PetscInt       *findices, *cindices;
8447   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8448   PetscInt        foffsets[32], coffsets[32];
8449   DMPolytopeType  ct;
8450   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8451   PetscErrorCode  ierr;
8452 
8453   PetscFunctionBegin;
8454   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8455   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8456   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8457   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8458   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8459   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8460   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8461   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8462   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8463   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8464   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8465   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8466   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8467   PetscCall(PetscArrayzero(foffsets, 32));
8468   PetscCall(PetscArrayzero(coffsets, 32));
8469   /* Column indices */
8470   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8471   maxFPoints = numCPoints;
8472   /* Compress out points not in the section */
8473   /*   TODO: Squeeze out points with 0 dof as well */
8474   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8475   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8476     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8477       cpoints[q * 2]     = cpoints[p];
8478       cpoints[q * 2 + 1] = cpoints[p + 1];
8479       ++q;
8480     }
8481   }
8482   numCPoints = q;
8483   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8484     PetscInt fdof;
8485 
8486     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8487     if (!dof) continue;
8488     for (f = 0; f < numFields; ++f) {
8489       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8490       coffsets[f + 1] += fdof;
8491     }
8492     numCIndices += dof;
8493   }
8494   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8495   /* Row indices */
8496   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8497   {
8498     DMPlexTransform tr;
8499     DMPolytopeType *rct;
8500     PetscInt       *rsize, *rcone, *rornt, Nt;
8501 
8502     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8503     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8504     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8505     numSubcells = rsize[Nt - 1];
8506     PetscCall(DMPlexTransformDestroy(&tr));
8507   }
8508   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8509   for (r = 0, q = 0; r < numSubcells; ++r) {
8510     /* TODO Map from coarse to fine cells */
8511     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8512     /* Compress out points not in the section */
8513     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8514     for (p = 0; p < numFPoints * 2; p += 2) {
8515       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8516         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8517         if (!dof) continue;
8518         for (s = 0; s < q; ++s)
8519           if (fpoints[p] == ftotpoints[s * 2]) break;
8520         if (s < q) continue;
8521         ftotpoints[q * 2]     = fpoints[p];
8522         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8523         ++q;
8524       }
8525     }
8526     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8527   }
8528   numFPoints = q;
8529   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8530     PetscInt fdof;
8531 
8532     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8533     if (!dof) continue;
8534     for (f = 0; f < numFields; ++f) {
8535       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8536       foffsets[f + 1] += fdof;
8537     }
8538     numFIndices += dof;
8539   }
8540   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8541 
8542   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8543   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8544   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8545   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8546   if (numFields) {
8547     const PetscInt **permsF[32] = {NULL};
8548     const PetscInt **permsC[32] = {NULL};
8549 
8550     for (f = 0; f < numFields; f++) {
8551       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8552       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8553     }
8554     for (p = 0; p < numFPoints; p++) {
8555       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8556       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8557     }
8558     for (p = 0; p < numCPoints; p++) {
8559       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8560       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8561     }
8562     for (f = 0; f < numFields; f++) {
8563       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8564       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8565     }
8566   } else {
8567     const PetscInt **permsF = NULL;
8568     const PetscInt **permsC = NULL;
8569 
8570     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8571     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8572     for (p = 0, off = 0; p < numFPoints; p++) {
8573       const PetscInt *perm = permsF ? permsF[p] : NULL;
8574 
8575       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8576       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8577     }
8578     for (p = 0, off = 0; p < numCPoints; p++) {
8579       const PetscInt *perm = permsC ? permsC[p] : NULL;
8580 
8581       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8582       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8583     }
8584     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8585     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8586   }
8587   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8588   /* TODO: flips */
8589   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8590   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8591   if (ierr) {
8592     PetscMPIInt rank;
8593 
8594     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8595     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8596     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8597     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8598     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8599   }
8600   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8601   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8602   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8603   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8604   PetscFunctionReturn(PETSC_SUCCESS);
8605 }
8606 
8607 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8608 {
8609   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8610   PetscInt       *cpoints      = NULL;
8611   PetscInt        foffsets[32] = {0}, coffsets[32] = {0};
8612   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8613   DMPolytopeType  ct;
8614   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8615 
8616   PetscFunctionBegin;
8617   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8618   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8619   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8620   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8621   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8622   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8623   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8624   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8625   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8626   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8627   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8628   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8629   /* Column indices */
8630   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8631   maxFPoints = numCPoints;
8632   /* Compress out points not in the section */
8633   /*   TODO: Squeeze out points with 0 dof as well */
8634   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8635   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8636     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8637       cpoints[q * 2]     = cpoints[p];
8638       cpoints[q * 2 + 1] = cpoints[p + 1];
8639       ++q;
8640     }
8641   }
8642   numCPoints = q;
8643   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8644     PetscInt fdof;
8645 
8646     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8647     if (!dof) continue;
8648     for (f = 0; f < numFields; ++f) {
8649       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8650       coffsets[f + 1] += fdof;
8651     }
8652     numCIndices += dof;
8653   }
8654   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8655   /* Row indices */
8656   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8657   {
8658     DMPlexTransform tr;
8659     DMPolytopeType *rct;
8660     PetscInt       *rsize, *rcone, *rornt, Nt;
8661 
8662     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8663     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8664     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8665     numSubcells = rsize[Nt - 1];
8666     PetscCall(DMPlexTransformDestroy(&tr));
8667   }
8668   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8669   for (r = 0, q = 0; r < numSubcells; ++r) {
8670     /* TODO Map from coarse to fine cells */
8671     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8672     /* Compress out points not in the section */
8673     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8674     for (p = 0; p < numFPoints * 2; p += 2) {
8675       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8676         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8677         if (!dof) continue;
8678         for (s = 0; s < q; ++s)
8679           if (fpoints[p] == ftotpoints[s * 2]) break;
8680         if (s < q) continue;
8681         ftotpoints[q * 2]     = fpoints[p];
8682         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8683         ++q;
8684       }
8685     }
8686     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8687   }
8688   numFPoints = q;
8689   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8690     PetscInt fdof;
8691 
8692     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8693     if (!dof) continue;
8694     for (f = 0; f < numFields; ++f) {
8695       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8696       foffsets[f + 1] += fdof;
8697     }
8698     numFIndices += dof;
8699   }
8700   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8701 
8702   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8703   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8704   if (numFields) {
8705     const PetscInt **permsF[32] = {NULL};
8706     const PetscInt **permsC[32] = {NULL};
8707 
8708     for (f = 0; f < numFields; f++) {
8709       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8710       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8711     }
8712     for (p = 0; p < numFPoints; p++) {
8713       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8714       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8715     }
8716     for (p = 0; p < numCPoints; p++) {
8717       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8718       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8719     }
8720     for (f = 0; f < numFields; f++) {
8721       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8722       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8723     }
8724   } else {
8725     const PetscInt **permsF = NULL;
8726     const PetscInt **permsC = NULL;
8727 
8728     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8729     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8730     for (p = 0, off = 0; p < numFPoints; p++) {
8731       const PetscInt *perm = permsF ? permsF[p] : NULL;
8732 
8733       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8734       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8735     }
8736     for (p = 0, off = 0; p < numCPoints; p++) {
8737       const PetscInt *perm = permsC ? permsC[p] : NULL;
8738 
8739       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8740       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8741     }
8742     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8743     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8744   }
8745   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8746   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8747   PetscFunctionReturn(PETSC_SUCCESS);
8748 }
8749 
8750 /*@
8751   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8752 
8753   Input Parameter:
8754 . dm - The `DMPLEX` object
8755 
8756   Output Parameter:
8757 . cellHeight - The height of a cell
8758 
8759   Level: developer
8760 
8761 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8762 @*/
8763 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8764 {
8765   DM_Plex *mesh = (DM_Plex *)dm->data;
8766 
8767   PetscFunctionBegin;
8768   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8769   PetscAssertPointer(cellHeight, 2);
8770   *cellHeight = mesh->vtkCellHeight;
8771   PetscFunctionReturn(PETSC_SUCCESS);
8772 }
8773 
8774 /*@
8775   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8776 
8777   Input Parameters:
8778 + dm         - The `DMPLEX` object
8779 - cellHeight - The height of a cell
8780 
8781   Level: developer
8782 
8783 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8784 @*/
8785 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8786 {
8787   DM_Plex *mesh = (DM_Plex *)dm->data;
8788 
8789   PetscFunctionBegin;
8790   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8791   mesh->vtkCellHeight = cellHeight;
8792   PetscFunctionReturn(PETSC_SUCCESS);
8793 }
8794 
8795 /*@
8796   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8797 
8798   Input Parameters:
8799 + dm - The `DMPLEX` object
8800 - ct - The `DMPolytopeType` of the cell
8801 
8802   Output Parameters:
8803 + start - The first cell of this type, or `NULL`
8804 - end   - The upper bound on this celltype, or `NULL`
8805 
8806   Level: advanced
8807 
8808 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8809 @*/
8810 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PeOp PetscInt *start, PeOp PetscInt *end)
8811 {
8812   DM_Plex *mesh = (DM_Plex *)dm->data;
8813   DMLabel  label;
8814   PetscInt pStart, pEnd;
8815 
8816   PetscFunctionBegin;
8817   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8818   if (start) {
8819     PetscAssertPointer(start, 3);
8820     *start = 0;
8821   }
8822   if (end) {
8823     PetscAssertPointer(end, 4);
8824     *end = 0;
8825   }
8826   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8827   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8828   if (mesh->tr) {
8829     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8830   } else {
8831     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8832     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8833     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8834   }
8835   PetscFunctionReturn(PETSC_SUCCESS);
8836 }
8837 
8838 /*@
8839   DMPlexGetDepthStratumGlobalSize - Get the global size for a given depth stratum
8840 
8841   Input Parameters:
8842 + dm    - The `DMPLEX` object
8843 - depth - The depth for the given point stratum
8844 
8845   Output Parameter:
8846 . gsize - The global number of points in the stratum
8847 
8848   Level: advanced
8849 
8850 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8851 @*/
8852 PetscErrorCode DMPlexGetDepthStratumGlobalSize(DM dm, PetscInt depth, PetscInt *gsize)
8853 {
8854   PetscSF         sf;
8855   const PetscInt *leaves;
8856   PetscInt        Nl, loc, start, end, lsize = 0;
8857 
8858   PetscFunctionBegin;
8859   PetscCall(DMGetPointSF(dm, &sf));
8860   PetscCall(PetscSFGetGraph(sf, NULL, &Nl, &leaves, NULL));
8861   PetscCall(DMPlexGetDepthStratum(dm, depth, &start, &end));
8862   for (PetscInt p = start; p < end; ++p) {
8863     PetscCall(PetscFindInt(p, Nl, leaves, &loc));
8864     if (loc < 0) ++lsize;
8865   }
8866   PetscCallMPI(MPI_Allreduce(&lsize, gsize, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
8867   PetscFunctionReturn(PETSC_SUCCESS);
8868 }
8869 
8870 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8871 {
8872   PetscSection section, globalSection;
8873   PetscInt    *numbers, p;
8874 
8875   PetscFunctionBegin;
8876   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8877   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8878   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8879   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8880   PetscCall(PetscSectionSetUp(section));
8881   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &globalSection));
8882   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8883   for (p = pStart; p < pEnd; ++p) {
8884     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8885     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8886     else numbers[p - pStart] += shift;
8887   }
8888   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8889   if (globalSize) {
8890     PetscLayout layout;
8891     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8892     PetscCall(PetscLayoutGetSize(layout, globalSize));
8893     PetscCall(PetscLayoutDestroy(&layout));
8894   }
8895   PetscCall(PetscSectionDestroy(&section));
8896   PetscCall(PetscSectionDestroy(&globalSection));
8897   PetscFunctionReturn(PETSC_SUCCESS);
8898 }
8899 
8900 /*@
8901   DMPlexCreateCellNumbering - Get a global cell numbering for all cells on this process
8902 
8903   Input Parameters:
8904 + dm         - The `DMPLEX` object
8905 - includeAll - Whether to include all cells, or just the simplex and box cells
8906 
8907   Output Parameter:
8908 . globalCellNumbers - Global cell numbers for all cells on this process
8909 
8910   Level: developer
8911 
8912 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`
8913 @*/
8914 PetscErrorCode DMPlexCreateCellNumbering(DM dm, PetscBool includeAll, IS *globalCellNumbers)
8915 {
8916   PetscInt cellHeight, cStart, cEnd;
8917 
8918   PetscFunctionBegin;
8919   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8920   if (includeAll) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8921   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8922   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8923   PetscFunctionReturn(PETSC_SUCCESS);
8924 }
8925 
8926 /*@
8927   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8928 
8929   Input Parameter:
8930 . dm - The `DMPLEX` object
8931 
8932   Output Parameter:
8933 . globalCellNumbers - Global cell numbers for all cells on this process
8934 
8935   Level: developer
8936 
8937 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateCellNumbering()`, `DMPlexGetVertexNumbering()`
8938 @*/
8939 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8940 {
8941   DM_Plex *mesh = (DM_Plex *)dm->data;
8942 
8943   PetscFunctionBegin;
8944   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8945   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8946   *globalCellNumbers = mesh->globalCellNumbers;
8947   PetscFunctionReturn(PETSC_SUCCESS);
8948 }
8949 
8950 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8951 {
8952   PetscInt vStart, vEnd;
8953 
8954   PetscFunctionBegin;
8955   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8956   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8957   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8958   PetscFunctionReturn(PETSC_SUCCESS);
8959 }
8960 
8961 /*@
8962   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8963 
8964   Input Parameter:
8965 . dm - The `DMPLEX` object
8966 
8967   Output Parameter:
8968 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8969 
8970   Level: developer
8971 
8972 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8973 @*/
8974 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8975 {
8976   DM_Plex *mesh = (DM_Plex *)dm->data;
8977 
8978   PetscFunctionBegin;
8979   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8980   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8981   *globalVertexNumbers = mesh->globalVertexNumbers;
8982   PetscFunctionReturn(PETSC_SUCCESS);
8983 }
8984 
8985 /*@
8986   DMPlexCreatePointNumbering - Create a global numbering for all points.
8987 
8988   Collective
8989 
8990   Input Parameter:
8991 . dm - The `DMPLEX` object
8992 
8993   Output Parameter:
8994 . globalPointNumbers - Global numbers for all points on this process
8995 
8996   Level: developer
8997 
8998   Notes:
8999   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
9000   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
9001   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
9002   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
9003 
9004   The partitioned mesh is
9005   ```
9006   (2)--0--(3)--1--(4)    (1)--0--(2)
9007   ```
9008   and its global numbering is
9009   ```
9010   (3)--0--(4)--1--(5)--2--(6)
9011   ```
9012   Then the global numbering is provided as
9013   ```
9014   [0] Number of indices in set 5
9015   [0] 0 0
9016   [0] 1 1
9017   [0] 2 3
9018   [0] 3 4
9019   [0] 4 -6
9020   [1] Number of indices in set 3
9021   [1] 0 2
9022   [1] 1 5
9023   [1] 2 6
9024   ```
9025 
9026 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
9027 @*/
9028 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
9029 {
9030   IS        nums[4];
9031   PetscInt  depths[4], gdepths[4], starts[4];
9032   PetscInt  depth, d, shift = 0;
9033   PetscBool empty = PETSC_FALSE;
9034 
9035   PetscFunctionBegin;
9036   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9037   PetscCall(DMPlexGetDepth(dm, &depth));
9038   // For unstratified meshes use dim instead of depth
9039   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
9040   // If any stratum is empty, we must mark all empty
9041   for (d = 0; d <= depth; ++d) {
9042     PetscInt end;
9043 
9044     depths[d] = depth - d;
9045     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
9046     if (!(starts[d] - end)) empty = PETSC_TRUE;
9047   }
9048   if (empty)
9049     for (d = 0; d <= depth; ++d) {
9050       depths[d] = -1;
9051       starts[d] = -1;
9052     }
9053   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
9054   PetscCallMPI(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
9055   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]);
9056   // Note here that 'shift' is collective, so that the numbering is stratified by depth
9057   for (d = 0; d <= depth; ++d) {
9058     PetscInt pStart, pEnd, gsize;
9059 
9060     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
9061     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
9062     shift += gsize;
9063   }
9064   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
9065   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
9066   PetscFunctionReturn(PETSC_SUCCESS);
9067 }
9068 
9069 /*@
9070   DMPlexCreateEdgeNumbering - Create a global numbering for edges.
9071 
9072   Collective
9073 
9074   Input Parameter:
9075 . dm - The `DMPLEX` object
9076 
9077   Output Parameter:
9078 . globalEdgeNumbers - Global numbers for all edges on this process
9079 
9080   Level: developer
9081 
9082   Notes:
9083   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).
9084 
9085 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`, `DMPlexCreatePointNumbering()`
9086 @*/
9087 PetscErrorCode DMPlexCreateEdgeNumbering(DM dm, IS *globalEdgeNumbers)
9088 {
9089   PetscSF  sf;
9090   PetscInt eStart, eEnd;
9091 
9092   PetscFunctionBegin;
9093   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9094   PetscCall(DMGetPointSF(dm, &sf));
9095   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9096   PetscCall(DMPlexCreateNumbering_Plex(dm, eStart, eEnd, 0, NULL, sf, globalEdgeNumbers));
9097   PetscFunctionReturn(PETSC_SUCCESS);
9098 }
9099 
9100 /*@
9101   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
9102 
9103   Input Parameter:
9104 . dm - The `DMPLEX` object
9105 
9106   Output Parameter:
9107 . ranks - The rank field
9108 
9109   Options Database Key:
9110 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
9111 
9112   Level: intermediate
9113 
9114 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
9115 @*/
9116 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
9117 {
9118   DM             rdm;
9119   PetscFE        fe;
9120   PetscScalar   *r;
9121   PetscMPIInt    rank;
9122   DMPolytopeType ct;
9123   PetscInt       dim, cStart, cEnd, c;
9124   PetscBool      simplex;
9125 
9126   PetscFunctionBeginUser;
9127   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9128   PetscAssertPointer(ranks, 2);
9129   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
9130   PetscCall(DMClone(dm, &rdm));
9131   PetscCall(DMGetDimension(rdm, &dim));
9132   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
9133   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
9134   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
9135   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
9136   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
9137   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9138   PetscCall(PetscFEDestroy(&fe));
9139   PetscCall(DMCreateDS(rdm));
9140   PetscCall(DMCreateGlobalVector(rdm, ranks));
9141   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
9142   PetscCall(VecGetArray(*ranks, &r));
9143   for (c = cStart; c < cEnd; ++c) {
9144     PetscScalar *lr;
9145 
9146     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
9147     if (lr) *lr = rank;
9148   }
9149   PetscCall(VecRestoreArray(*ranks, &r));
9150   PetscCall(DMDestroy(&rdm));
9151   PetscFunctionReturn(PETSC_SUCCESS);
9152 }
9153 
9154 /*@
9155   DMPlexCreateLabelField - Create a field whose value is the label value for that point
9156 
9157   Input Parameters:
9158 + dm    - The `DMPLEX`
9159 - label - The `DMLabel`
9160 
9161   Output Parameter:
9162 . val - The label value field
9163 
9164   Options Database Key:
9165 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
9166 
9167   Level: intermediate
9168 
9169 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
9170 @*/
9171 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
9172 {
9173   DM             rdm, plex;
9174   Vec            lval;
9175   PetscSection   section;
9176   PetscFE        fe;
9177   PetscScalar   *v;
9178   PetscInt       dim, pStart, pEnd, p, cStart;
9179   DMPolytopeType ct;
9180   char           name[PETSC_MAX_PATH_LEN];
9181   const char    *lname, *prefix;
9182 
9183   PetscFunctionBeginUser;
9184   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9185   PetscAssertPointer(label, 2);
9186   PetscAssertPointer(val, 3);
9187   PetscCall(DMClone(dm, &rdm));
9188   PetscCall(DMConvert(rdm, DMPLEX, &plex));
9189   PetscCall(DMPlexGetHeightStratum(plex, 0, &cStart, NULL));
9190   PetscCall(DMPlexGetCellType(plex, cStart, &ct));
9191   PetscCall(DMDestroy(&plex));
9192   PetscCall(DMGetDimension(rdm, &dim));
9193   PetscCall(DMGetOptionsPrefix(dm, &prefix));
9194   PetscCall(PetscObjectGetName((PetscObject)label, &lname));
9195   PetscCall(PetscSNPrintf(name, sizeof(name), "%s%s_", prefix ? prefix : "", lname));
9196   PetscCall(PetscFECreateByCell(PETSC_COMM_SELF, dim, 1, ct, name, -1, &fe));
9197   PetscCall(PetscObjectSetName((PetscObject)fe, ""));
9198   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9199   PetscCall(PetscFEDestroy(&fe));
9200   PetscCall(DMCreateDS(rdm));
9201   PetscCall(DMCreateGlobalVector(rdm, val));
9202   PetscCall(DMCreateLocalVector(rdm, &lval));
9203   PetscCall(PetscObjectSetName((PetscObject)*val, lname));
9204   PetscCall(DMGetLocalSection(rdm, &section));
9205   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
9206   PetscCall(VecGetArray(lval, &v));
9207   for (p = pStart; p < pEnd; ++p) {
9208     PetscInt cval, dof, off;
9209 
9210     PetscCall(PetscSectionGetDof(section, p, &dof));
9211     if (!dof) continue;
9212     PetscCall(DMLabelGetValue(label, p, &cval));
9213     PetscCall(PetscSectionGetOffset(section, p, &off));
9214     for (PetscInt d = 0; d < dof; d++) v[off + d] = cval;
9215   }
9216   PetscCall(VecRestoreArray(lval, &v));
9217   PetscCall(DMLocalToGlobal(rdm, lval, INSERT_VALUES, *val));
9218   PetscCall(VecDestroy(&lval));
9219   PetscCall(DMDestroy(&rdm));
9220   PetscFunctionReturn(PETSC_SUCCESS);
9221 }
9222 
9223 /*@
9224   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
9225 
9226   Input Parameter:
9227 . dm - The `DMPLEX` object
9228 
9229   Level: developer
9230 
9231   Notes:
9232   This is a useful diagnostic when creating meshes programmatically.
9233 
9234   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9235 
9236 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9237 @*/
9238 PetscErrorCode DMPlexCheckSymmetry(DM dm)
9239 {
9240   PetscSection    coneSection, supportSection;
9241   const PetscInt *cone, *support;
9242   PetscInt        coneSize, c, supportSize, s;
9243   PetscInt        pStart, pEnd, p, pp, csize, ssize;
9244   PetscBool       storagecheck = PETSC_TRUE;
9245 
9246   PetscFunctionBegin;
9247   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9248   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
9249   PetscCall(DMPlexGetConeSection(dm, &coneSection));
9250   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
9251   /* Check that point p is found in the support of its cone points, and vice versa */
9252   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9253   for (p = pStart; p < pEnd; ++p) {
9254     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
9255     PetscCall(DMPlexGetCone(dm, p, &cone));
9256     for (c = 0; c < coneSize; ++c) {
9257       PetscBool dup = PETSC_FALSE;
9258       PetscInt  d;
9259       for (d = c - 1; d >= 0; --d) {
9260         if (cone[c] == cone[d]) {
9261           dup = PETSC_TRUE;
9262           break;
9263         }
9264       }
9265       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
9266       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
9267       for (s = 0; s < supportSize; ++s) {
9268         if (support[s] == p) break;
9269       }
9270       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
9271         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
9272         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
9273         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9274         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
9275         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
9276         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9277         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]);
9278         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
9279       }
9280     }
9281     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
9282     if (p != pp) {
9283       storagecheck = PETSC_FALSE;
9284       continue;
9285     }
9286     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
9287     PetscCall(DMPlexGetSupport(dm, p, &support));
9288     for (s = 0; s < supportSize; ++s) {
9289       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
9290       PetscCall(DMPlexGetCone(dm, support[s], &cone));
9291       for (c = 0; c < coneSize; ++c) {
9292         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
9293         if (cone[c] != pp) {
9294           c = 0;
9295           break;
9296         }
9297         if (cone[c] == p) break;
9298       }
9299       if (c >= coneSize) {
9300         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
9301         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
9302         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9303         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
9304         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
9305         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9306         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
9307       }
9308     }
9309   }
9310   if (storagecheck) {
9311     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
9312     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
9313     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
9314   }
9315   PetscFunctionReturn(PETSC_SUCCESS);
9316 }
9317 
9318 /*
9319   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.
9320 */
9321 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
9322 {
9323   DMPolytopeType  cct;
9324   PetscInt        ptpoints[4];
9325   const PetscInt *cone, *ccone, *ptcone;
9326   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
9327 
9328   PetscFunctionBegin;
9329   *unsplit = 0;
9330   switch (ct) {
9331   case DM_POLYTOPE_POINT_PRISM_TENSOR:
9332     ptpoints[npt++] = c;
9333     break;
9334   case DM_POLYTOPE_SEG_PRISM_TENSOR:
9335     PetscCall(DMPlexGetCone(dm, c, &cone));
9336     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9337     for (cp = 0; cp < coneSize; ++cp) {
9338       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
9339       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
9340     }
9341     break;
9342   case DM_POLYTOPE_TRI_PRISM_TENSOR:
9343   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9344     PetscCall(DMPlexGetCone(dm, c, &cone));
9345     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9346     for (cp = 0; cp < coneSize; ++cp) {
9347       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
9348       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
9349       for (ccp = 0; ccp < cconeSize; ++ccp) {
9350         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
9351         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
9352           PetscInt p;
9353           for (p = 0; p < npt; ++p)
9354             if (ptpoints[p] == ccone[ccp]) break;
9355           if (p == npt) ptpoints[npt++] = ccone[ccp];
9356         }
9357       }
9358     }
9359     break;
9360   default:
9361     break;
9362   }
9363   for (pt = 0; pt < npt; ++pt) {
9364     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
9365     if (ptcone[0] == ptcone[1]) ++(*unsplit);
9366   }
9367   PetscFunctionReturn(PETSC_SUCCESS);
9368 }
9369 
9370 /*@
9371   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
9372 
9373   Input Parameters:
9374 + dm         - The `DMPLEX` object
9375 - cellHeight - Normally 0
9376 
9377   Level: developer
9378 
9379   Notes:
9380   This is a useful diagnostic when creating meshes programmatically.
9381   Currently applicable only to homogeneous simplex or tensor meshes.
9382 
9383   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9384 
9385 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9386 @*/
9387 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
9388 {
9389   DMPlexInterpolatedFlag interp;
9390   DMPolytopeType         ct;
9391   PetscInt               vStart, vEnd, cStart, cEnd, c;
9392 
9393   PetscFunctionBegin;
9394   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9395   PetscCall(DMPlexIsInterpolated(dm, &interp));
9396   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9397   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9398   for (c = cStart; c < cEnd; ++c) {
9399     PetscInt *closure = NULL;
9400     PetscInt  coneSize, closureSize, cl, Nv = 0;
9401 
9402     PetscCall(DMPlexGetCellType(dm, c, &ct));
9403     if (ct == DM_POLYTOPE_UNKNOWN) continue;
9404     if (interp == DMPLEX_INTERPOLATED_FULL) {
9405       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9406       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));
9407     }
9408     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9409     for (cl = 0; cl < closureSize * 2; cl += 2) {
9410       const PetscInt p = closure[cl];
9411       if ((p >= vStart) && (p < vEnd)) ++Nv;
9412     }
9413     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9414     /* Special Case: Tensor faces with identified vertices */
9415     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
9416       PetscInt unsplit;
9417 
9418       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9419       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
9420     }
9421     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));
9422   }
9423   PetscFunctionReturn(PETSC_SUCCESS);
9424 }
9425 
9426 /*@
9427   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
9428 
9429   Collective
9430 
9431   Input Parameters:
9432 + dm         - The `DMPLEX` object
9433 - cellHeight - Normally 0
9434 
9435   Level: developer
9436 
9437   Notes:
9438   This is a useful diagnostic when creating meshes programmatically.
9439   This routine is only relevant for meshes that are fully interpolated across all ranks.
9440   It will error out if a partially interpolated mesh is given on some rank.
9441   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
9442 
9443   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9444 
9445 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
9446 @*/
9447 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
9448 {
9449   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
9450   DMPlexInterpolatedFlag interpEnum;
9451 
9452   PetscFunctionBegin;
9453   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9454   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
9455   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
9456   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
9457     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
9458     PetscFunctionReturn(PETSC_SUCCESS);
9459   }
9460 
9461   PetscCall(DMGetDimension(dm, &dim));
9462   PetscCall(DMPlexGetDepth(dm, &depth));
9463   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9464   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
9465     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
9466     for (c = cStart; c < cEnd; ++c) {
9467       const PetscInt       *cone, *ornt, *faceSizes, *faces;
9468       const DMPolytopeType *faceTypes;
9469       DMPolytopeType        ct;
9470       PetscInt              numFaces, coneSize, f;
9471       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
9472 
9473       PetscCall(DMPlexGetCellType(dm, c, &ct));
9474       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9475       if (unsplit) continue;
9476       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9477       PetscCall(DMPlexGetCone(dm, c, &cone));
9478       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
9479       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9480       for (cl = 0; cl < closureSize * 2; cl += 2) {
9481         const PetscInt p = closure[cl];
9482         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
9483       }
9484       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9485       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);
9486       for (f = 0; f < numFaces; ++f) {
9487         DMPolytopeType fct;
9488         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
9489 
9490         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
9491         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
9492         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
9493           const PetscInt p = fclosure[cl];
9494           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
9495         }
9496         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]);
9497         for (v = 0; v < fnumCorners; ++v) {
9498           if (fclosure[v] != faces[fOff + v]) {
9499             PetscInt v1;
9500 
9501             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
9502             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
9503             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
9504             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
9505             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9506             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]);
9507           }
9508         }
9509         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
9510         fOff += faceSizes[f];
9511       }
9512       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9513       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9514     }
9515   }
9516   PetscFunctionReturn(PETSC_SUCCESS);
9517 }
9518 
9519 /*@
9520   DMPlexCheckGeometry - Check the geometry of mesh cells
9521 
9522   Input Parameter:
9523 . dm - The `DMPLEX` object
9524 
9525   Level: developer
9526 
9527   Notes:
9528   This is a useful diagnostic when creating meshes programmatically.
9529 
9530   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9531 
9532 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9533 @*/
9534 PetscErrorCode DMPlexCheckGeometry(DM dm)
9535 {
9536   Vec       coordinates;
9537   PetscReal detJ, J[9], refVol = 1.0;
9538   PetscReal vol;
9539   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9540 
9541   PetscFunctionBegin;
9542   PetscCall(DMGetDimension(dm, &dim));
9543   PetscCall(DMGetCoordinateDim(dm, &dE));
9544   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9545   PetscCall(DMPlexGetDepth(dm, &depth));
9546   for (d = 0; d < dim; ++d) refVol *= 2.0;
9547   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9548   /* Make sure local coordinates are created, because that step is collective */
9549   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9550   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9551   for (c = cStart; c < cEnd; ++c) {
9552     DMPolytopeType ct;
9553     PetscInt       unsplit;
9554     PetscBool      ignoreZeroVol = PETSC_FALSE;
9555 
9556     PetscCall(DMPlexGetCellType(dm, c, &ct));
9557     switch (ct) {
9558     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9559     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9560     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9561       ignoreZeroVol = PETSC_TRUE;
9562       break;
9563     default:
9564       break;
9565     }
9566     switch (ct) {
9567     case DM_POLYTOPE_TRI_PRISM:
9568     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9569     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9570     case DM_POLYTOPE_PYRAMID:
9571       continue;
9572     default:
9573       break;
9574     }
9575     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9576     if (unsplit) continue;
9577     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9578     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);
9579     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9580     /* This should work with periodicity since DG coordinates should be used */
9581     if (depth > 1) {
9582       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9583       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);
9584       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9585     }
9586   }
9587   PetscFunctionReturn(PETSC_SUCCESS);
9588 }
9589 
9590 /*@
9591   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9592 
9593   Collective
9594 
9595   Input Parameters:
9596 + dm              - The `DMPLEX` object
9597 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9598 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9599 
9600   Level: developer
9601 
9602   Notes:
9603   This is mainly intended for debugging/testing purposes.
9604 
9605   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9606 
9607   Extra roots can come from periodic cuts, where additional points appear on the boundary
9608 
9609 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9610 @*/
9611 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9612 {
9613   PetscInt           l, nleaves, nroots, overlap;
9614   const PetscInt    *locals;
9615   const PetscSFNode *remotes;
9616   PetscBool          distributed;
9617   MPI_Comm           comm;
9618   PetscMPIInt        rank;
9619 
9620   PetscFunctionBegin;
9621   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9622   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9623   else pointSF = dm->sf;
9624   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9625   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9626   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9627   {
9628     PetscMPIInt mpiFlag;
9629 
9630     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9631     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9632   }
9633   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9634   PetscCall(DMPlexIsDistributed(dm, &distributed));
9635   if (!distributed) {
9636     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);
9637     PetscFunctionReturn(PETSC_SUCCESS);
9638   }
9639   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);
9640   PetscCall(DMPlexGetOverlap(dm, &overlap));
9641 
9642   /* Check SF graph is compatible with DMPlex chart */
9643   {
9644     PetscInt pStart, pEnd, maxLeaf;
9645 
9646     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9647     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9648     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9649     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9650   }
9651 
9652   /* Check there are no cells in interface */
9653   if (!overlap) {
9654     PetscInt cellHeight, cStart, cEnd;
9655 
9656     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9657     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9658     for (l = 0; l < nleaves; ++l) {
9659       const PetscInt point = locals ? locals[l] : l;
9660 
9661       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9662     }
9663   }
9664 
9665   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9666   {
9667     const PetscInt *rootdegree;
9668 
9669     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9670     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9671     for (l = 0; l < nleaves; ++l) {
9672       const PetscInt  point = locals ? locals[l] : l;
9673       const PetscInt *cone;
9674       PetscInt        coneSize, c, idx;
9675 
9676       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9677       PetscCall(DMPlexGetCone(dm, point, &cone));
9678       for (c = 0; c < coneSize; ++c) {
9679         if (!rootdegree[cone[c]]) {
9680           if (locals) {
9681             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9682           } else {
9683             idx = (cone[c] < nleaves) ? cone[c] : -1;
9684           }
9685           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9686         }
9687       }
9688     }
9689   }
9690   PetscFunctionReturn(PETSC_SUCCESS);
9691 }
9692 
9693 /*@
9694   DMPlexCheckOrphanVertices - Check that no vertices are disconnected from the mesh, unless the mesh only consists of disconnected vertices.
9695 
9696   Collective
9697 
9698   Input Parameter:
9699 . dm - The `DMPLEX` object
9700 
9701   Level: developer
9702 
9703   Notes:
9704   This is mainly intended for debugging/testing purposes.
9705 
9706   Other cell types which are disconnected would be caught by the symmetry and face checks.
9707 
9708   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9709 
9710 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheck()`, `DMSetFromOptions()`
9711 @*/
9712 PetscErrorCode DMPlexCheckOrphanVertices(DM dm)
9713 {
9714   PetscInt pStart, pEnd, vStart, vEnd;
9715 
9716   PetscFunctionBegin;
9717   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9718   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9719   if (pStart == vStart && pEnd == vEnd) PetscFunctionReturn(PETSC_SUCCESS);
9720   for (PetscInt v = vStart; v < vEnd; ++v) {
9721     PetscInt suppSize;
9722 
9723     PetscCall(DMPlexGetSupportSize(dm, v, &suppSize));
9724     PetscCheck(suppSize, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Vertex %" PetscInt_FMT " is disconnected from the mesh", v);
9725   }
9726   PetscFunctionReturn(PETSC_SUCCESS);
9727 }
9728 
9729 /*@
9730   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9731 
9732   Input Parameter:
9733 . dm - The `DMPLEX` object
9734 
9735   Level: developer
9736 
9737   Notes:
9738   This is a useful diagnostic when creating meshes programmatically.
9739 
9740   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9741 
9742   Currently does not include `DMPlexCheckCellShape()`.
9743 
9744 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9745 @*/
9746 PetscErrorCode DMPlexCheck(DM dm)
9747 {
9748   PetscInt cellHeight;
9749 
9750   PetscFunctionBegin;
9751   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9752   PetscCall(DMPlexCheckSymmetry(dm));
9753   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9754   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9755   PetscCall(DMPlexCheckGeometry(dm));
9756   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9757   PetscCall(DMPlexCheckInterfaceCones(dm));
9758   PetscCall(DMPlexCheckOrphanVertices(dm));
9759   PetscFunctionReturn(PETSC_SUCCESS);
9760 }
9761 
9762 typedef struct cell_stats {
9763   PetscReal min, max, sum, squaresum;
9764   PetscInt  count;
9765 } cell_stats_t;
9766 
9767 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9768 {
9769   PetscInt i, N = *len;
9770 
9771   for (i = 0; i < N; i++) {
9772     cell_stats_t *A = (cell_stats_t *)a;
9773     cell_stats_t *B = (cell_stats_t *)b;
9774 
9775     B->min = PetscMin(A->min, B->min);
9776     B->max = PetscMax(A->max, B->max);
9777     B->sum += A->sum;
9778     B->squaresum += A->squaresum;
9779     B->count += A->count;
9780   }
9781 }
9782 
9783 /*@
9784   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9785 
9786   Collective
9787 
9788   Input Parameters:
9789 + dm        - The `DMPLEX` object
9790 . output    - If true, statistics will be displayed on `stdout`
9791 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9792 
9793   Level: developer
9794 
9795   Notes:
9796   This is mainly intended for debugging/testing purposes.
9797 
9798   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9799 
9800 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9801 @*/
9802 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9803 {
9804   DM           dmCoarse;
9805   cell_stats_t stats, globalStats;
9806   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9807   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9808   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9809   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9810   PetscMPIInt  rank, size;
9811 
9812   PetscFunctionBegin;
9813   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9814   stats.min = PETSC_MAX_REAL;
9815   stats.max = PETSC_MIN_REAL;
9816   stats.sum = stats.squaresum = 0.;
9817   stats.count                 = 0;
9818 
9819   PetscCallMPI(MPI_Comm_size(comm, &size));
9820   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9821   PetscCall(DMGetCoordinateDim(dm, &cdim));
9822   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9823   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9824   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9825   for (c = cStart; c < cEnd; c++) {
9826     PetscInt  i;
9827     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9828 
9829     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9830     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9831     for (i = 0; i < PetscSqr(cdim); ++i) {
9832       frobJ += J[i] * J[i];
9833       frobInvJ += invJ[i] * invJ[i];
9834     }
9835     cond2 = frobJ * frobInvJ;
9836     cond  = PetscSqrtReal(cond2);
9837 
9838     stats.min = PetscMin(stats.min, cond);
9839     stats.max = PetscMax(stats.max, cond);
9840     stats.sum += cond;
9841     stats.squaresum += cond2;
9842     stats.count++;
9843     if (output && cond > limit) {
9844       PetscSection coordSection;
9845       Vec          coordsLocal;
9846       PetscScalar *coords = NULL;
9847       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9848 
9849       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9850       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9851       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9852       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9853       for (i = 0; i < Nv / cdim; ++i) {
9854         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9855         for (d = 0; d < cdim; ++d) {
9856           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9857           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9858         }
9859         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9860       }
9861       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9862       for (cl = 0; cl < clSize * 2; cl += 2) {
9863         const PetscInt edge = closure[cl];
9864 
9865         if ((edge >= eStart) && (edge < eEnd)) {
9866           PetscReal len;
9867 
9868           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9869           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9870         }
9871       }
9872       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9873       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9874     }
9875   }
9876   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9877 
9878   if (size > 1) {
9879     PetscMPIInt  blockLengths[2] = {4, 1};
9880     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9881     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9882     MPI_Op       statReduce;
9883 
9884     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9885     PetscCallMPI(MPI_Type_commit(&statType));
9886     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9887     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9888     PetscCallMPI(MPI_Op_free(&statReduce));
9889     PetscCallMPI(MPI_Type_free(&statType));
9890   } else {
9891     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9892   }
9893   if (rank == 0) {
9894     count = globalStats.count;
9895     min   = globalStats.min;
9896     max   = globalStats.max;
9897     mean  = globalStats.sum / globalStats.count;
9898     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9899   }
9900 
9901   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));
9902   PetscCall(PetscFree2(J, invJ));
9903 
9904   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9905   if (dmCoarse) {
9906     PetscBool isplex;
9907 
9908     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9909     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9910   }
9911   PetscFunctionReturn(PETSC_SUCCESS);
9912 }
9913 
9914 /*@
9915   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9916   orthogonal quality below given tolerance.
9917 
9918   Collective
9919 
9920   Input Parameters:
9921 + dm   - The `DMPLEX` object
9922 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9923 - atol - [0, 1] Absolute tolerance for tagging cells.
9924 
9925   Output Parameters:
9926 + OrthQual      - `Vec` containing orthogonal quality per cell
9927 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9928 
9929   Options Database Keys:
9930 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9931 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9932 
9933   Level: intermediate
9934 
9935   Notes:
9936   Orthogonal quality is given by the following formula\:
9937 
9938   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9939 
9940   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
9941   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9942   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9943   calculating the cosine of the angle between these vectors.
9944 
9945   Orthogonal quality ranges from 1 (best) to 0 (worst).
9946 
9947   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9948   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9949 
9950   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9951 
9952 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9953 @*/
9954 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PeOp PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9955 {
9956   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9957   PetscInt              *idx;
9958   PetscScalar           *oqVals;
9959   const PetscScalar     *cellGeomArr, *faceGeomArr;
9960   PetscReal             *ci, *fi, *Ai;
9961   MPI_Comm               comm;
9962   Vec                    cellgeom, facegeom;
9963   DM                     dmFace, dmCell;
9964   IS                     glob;
9965   ISLocalToGlobalMapping ltog;
9966   PetscViewer            vwr;
9967 
9968   PetscFunctionBegin;
9969   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9970   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9971   PetscAssertPointer(OrthQual, 4);
9972   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9973   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9974   PetscCall(DMGetDimension(dm, &nc));
9975   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9976   {
9977     DMPlexInterpolatedFlag interpFlag;
9978 
9979     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9980     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9981       PetscMPIInt rank;
9982 
9983       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9984       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9985     }
9986   }
9987   if (OrthQualLabel) {
9988     PetscAssertPointer(OrthQualLabel, 5);
9989     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9990     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9991   } else {
9992     *OrthQualLabel = NULL;
9993   }
9994   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9995   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9996   PetscCall(DMPlexCreateCellNumbering(dm, PETSC_TRUE, &glob));
9997   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9998   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9999   PetscCall(VecCreate(comm, OrthQual));
10000   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
10001   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
10002   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
10003   PetscCall(VecSetUp(*OrthQual));
10004   PetscCall(ISDestroy(&glob));
10005   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
10006   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
10007   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
10008   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
10009   PetscCall(VecGetDM(cellgeom, &dmCell));
10010   PetscCall(VecGetDM(facegeom, &dmFace));
10011   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
10012   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
10013     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
10014     PetscInt         cellarr[2], *adj = NULL;
10015     PetscScalar     *cArr, *fArr;
10016     PetscReal        minvalc = 1.0, minvalf = 1.0;
10017     PetscFVCellGeom *cg;
10018 
10019     idx[cellIter] = cell - cStart;
10020     cellarr[0]    = cell;
10021     /* Make indexing into cellGeom easier */
10022     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
10023     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
10024     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
10025     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
10026     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
10027       PetscInt         i;
10028       const PetscInt   neigh  = adj[cellneigh];
10029       PetscReal        normci = 0, normfi = 0, normai = 0;
10030       PetscFVCellGeom *cgneigh;
10031       PetscFVFaceGeom *fg;
10032 
10033       /* Don't count ourselves in the neighbor list */
10034       if (neigh == cell) continue;
10035       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
10036       cellarr[1] = neigh;
10037       {
10038         PetscInt        numcovpts;
10039         const PetscInt *covpts;
10040 
10041         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
10042         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
10043         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
10044       }
10045 
10046       /* Compute c_i, f_i and their norms */
10047       for (i = 0; i < nc; i++) {
10048         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
10049         fi[i] = fg->centroid[i] - cg->centroid[i];
10050         Ai[i] = fg->normal[i];
10051         normci += PetscPowReal(ci[i], 2);
10052         normfi += PetscPowReal(fi[i], 2);
10053         normai += PetscPowReal(Ai[i], 2);
10054       }
10055       normci = PetscSqrtReal(normci);
10056       normfi = PetscSqrtReal(normfi);
10057       normai = PetscSqrtReal(normai);
10058 
10059       /* Normalize and compute for each face-cell-normal pair */
10060       for (i = 0; i < nc; i++) {
10061         ci[i] = ci[i] / normci;
10062         fi[i] = fi[i] / normfi;
10063         Ai[i] = Ai[i] / normai;
10064         /* PetscAbs because I don't know if normals are guaranteed to point out */
10065         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
10066         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
10067       }
10068       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
10069       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
10070     }
10071     PetscCall(PetscFree(adj));
10072     PetscCall(PetscFree2(cArr, fArr));
10073     /* Defer to cell if they're equal */
10074     oqVals[cellIter] = PetscMin(minvalf, minvalc);
10075     if (OrthQualLabel) {
10076       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
10077     }
10078   }
10079   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
10080   PetscCall(VecAssemblyBegin(*OrthQual));
10081   PetscCall(VecAssemblyEnd(*OrthQual));
10082   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
10083   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
10084   PetscCall(PetscOptionsCreateViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
10085   if (OrthQualLabel) {
10086     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
10087   }
10088   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
10089   PetscCall(PetscViewerDestroy(&vwr));
10090   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
10091   PetscFunctionReturn(PETSC_SUCCESS);
10092 }
10093 
10094 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
10095  * interpolator construction */
10096 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
10097 {
10098   PetscSection section, newSection, gsection;
10099   PetscSF      sf;
10100   PetscBool    hasConstraints, ghasConstraints;
10101 
10102   PetscFunctionBegin;
10103   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10104   PetscAssertPointer(odm, 2);
10105   PetscCall(DMGetLocalSection(dm, &section));
10106   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
10107   PetscCallMPI(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
10108   if (!ghasConstraints) {
10109     PetscCall(PetscObjectReference((PetscObject)dm));
10110     *odm = dm;
10111     PetscFunctionReturn(PETSC_SUCCESS);
10112   }
10113   PetscCall(DMClone(dm, odm));
10114   PetscCall(DMCopyFields(dm, PETSC_DETERMINE, PETSC_DETERMINE, *odm));
10115   PetscCall(DMGetLocalSection(*odm, &newSection));
10116   PetscCall(DMGetPointSF(*odm, &sf));
10117   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_TRUE, PETSC_FALSE, &gsection));
10118   PetscCall(DMSetGlobalSection(*odm, gsection));
10119   PetscCall(PetscSectionDestroy(&gsection));
10120   PetscFunctionReturn(PETSC_SUCCESS);
10121 }
10122 
10123 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
10124 {
10125   DM        dmco, dmfo;
10126   Mat       interpo;
10127   Vec       rscale;
10128   Vec       cglobalo, clocal;
10129   Vec       fglobal, fglobalo, flocal;
10130   PetscBool regular;
10131 
10132   PetscFunctionBegin;
10133   PetscCall(DMGetFullDM(dmc, &dmco));
10134   PetscCall(DMGetFullDM(dmf, &dmfo));
10135   PetscCall(DMSetCoarseDM(dmfo, dmco));
10136   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
10137   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
10138   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
10139   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
10140   PetscCall(DMCreateLocalVector(dmc, &clocal));
10141   PetscCall(VecSet(cglobalo, 0.));
10142   PetscCall(VecSet(clocal, 0.));
10143   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
10144   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
10145   PetscCall(DMCreateLocalVector(dmf, &flocal));
10146   PetscCall(VecSet(fglobal, 0.));
10147   PetscCall(VecSet(fglobalo, 0.));
10148   PetscCall(VecSet(flocal, 0.));
10149   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
10150   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
10151   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
10152   PetscCall(MatMult(interpo, cglobalo, fglobalo));
10153   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
10154   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
10155   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
10156   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
10157   *shift = fglobal;
10158   PetscCall(VecDestroy(&flocal));
10159   PetscCall(VecDestroy(&fglobalo));
10160   PetscCall(VecDestroy(&clocal));
10161   PetscCall(VecDestroy(&cglobalo));
10162   PetscCall(VecDestroy(&rscale));
10163   PetscCall(MatDestroy(&interpo));
10164   PetscCall(DMDestroy(&dmfo));
10165   PetscCall(DMDestroy(&dmco));
10166   PetscFunctionReturn(PETSC_SUCCESS);
10167 }
10168 
10169 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
10170 {
10171   PetscObject shifto;
10172   Vec         shift;
10173 
10174   PetscFunctionBegin;
10175   if (!interp) {
10176     Vec rscale;
10177 
10178     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
10179     PetscCall(VecDestroy(&rscale));
10180   } else {
10181     PetscCall(PetscObjectReference((PetscObject)interp));
10182   }
10183   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
10184   if (!shifto) {
10185     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
10186     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
10187     shifto = (PetscObject)shift;
10188     PetscCall(VecDestroy(&shift));
10189   }
10190   shift = (Vec)shifto;
10191   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
10192   PetscCall(VecAXPY(fineSol, 1.0, shift));
10193   PetscCall(MatDestroy(&interp));
10194   PetscFunctionReturn(PETSC_SUCCESS);
10195 }
10196 
10197 /* Pointwise interpolation
10198      Just code FEM for now
10199      u^f = I u^c
10200      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
10201      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
10202      I_{ij} = psi^f_i phi^c_j
10203 */
10204 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
10205 {
10206   PetscSection gsc, gsf;
10207   PetscInt     m, n;
10208   void        *ctx;
10209   DM           cdm;
10210   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
10211 
10212   PetscFunctionBegin;
10213   PetscCall(DMGetGlobalSection(dmFine, &gsf));
10214   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10215   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10216   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10217 
10218   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
10219   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
10220   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10221   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
10222   PetscCall(DMGetApplicationContext(dmFine, &ctx));
10223 
10224   PetscCall(DMGetCoarseDM(dmFine, &cdm));
10225   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10226   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
10227   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
10228   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
10229   if (scaling) {
10230     /* Use naive scaling */
10231     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
10232   }
10233   PetscFunctionReturn(PETSC_SUCCESS);
10234 }
10235 
10236 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
10237 {
10238   VecScatter ctx;
10239 
10240   PetscFunctionBegin;
10241   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
10242   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
10243   PetscCall(VecScatterDestroy(&ctx));
10244   PetscFunctionReturn(PETSC_SUCCESS);
10245 }
10246 
10247 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[])
10248 {
10249   const PetscInt f  = (PetscInt)PetscRealPart(constants[numConstants]);
10250   const PetscInt Nc = uOff[f + 1] - uOff[f];
10251   for (PetscInt c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
10252 }
10253 
10254 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *lmass, Vec *mass)
10255 {
10256   DM           dmc;
10257   PetscDS      ds;
10258   Vec          ones, locmass;
10259   IS           cellIS;
10260   PetscFormKey key;
10261   PetscInt     depth;
10262 
10263   PetscFunctionBegin;
10264   PetscCall(DMClone(dm, &dmc));
10265   PetscCall(DMCopyDisc(dm, dmc));
10266   PetscCall(DMGetDS(dmc, &ds));
10267   for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10268   if (mass) PetscCall(DMCreateGlobalVector(dm, mass));
10269   if (lmass) PetscCall(DMCreateLocalVector(dm, &locmass));
10270   else PetscCall(DMGetLocalVector(dm, &locmass));
10271   PetscCall(DMGetLocalVector(dm, &ones));
10272   PetscCall(DMPlexGetDepth(dm, &depth));
10273   PetscCall(DMGetStratumIS(dm, "depth", depth, &cellIS));
10274   PetscCall(VecSet(locmass, 0.0));
10275   PetscCall(VecSet(ones, 1.0));
10276   key.label = NULL;
10277   key.value = 0;
10278   key.field = 0;
10279   key.part  = 0;
10280   PetscCall(DMPlexComputeJacobianActionByKey(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
10281   PetscCall(ISDestroy(&cellIS));
10282   if (mass) {
10283     PetscCall(DMLocalToGlobalBegin(dm, locmass, ADD_VALUES, *mass));
10284     PetscCall(DMLocalToGlobalEnd(dm, locmass, ADD_VALUES, *mass));
10285   }
10286   PetscCall(DMRestoreLocalVector(dm, &ones));
10287   if (lmass) *lmass = locmass;
10288   else PetscCall(DMRestoreLocalVector(dm, &locmass));
10289   PetscCall(DMDestroy(&dmc));
10290   PetscFunctionReturn(PETSC_SUCCESS);
10291 }
10292 
10293 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
10294 {
10295   PetscSection gsc, gsf;
10296   PetscInt     m, n;
10297   void        *ctx;
10298   DM           cdm;
10299   PetscBool    regular;
10300 
10301   PetscFunctionBegin;
10302   if (dmFine == dmCoarse) {
10303     DM            dmc;
10304     PetscDS       ds;
10305     PetscWeakForm wf;
10306     Vec           u;
10307     IS            cellIS;
10308     PetscFormKey  key;
10309     PetscInt      depth;
10310 
10311     PetscCall(DMClone(dmFine, &dmc));
10312     PetscCall(DMCopyDisc(dmFine, dmc));
10313     PetscCall(DMGetDS(dmc, &ds));
10314     PetscCall(PetscDSGetWeakForm(ds, &wf));
10315     PetscCall(PetscWeakFormClear(wf));
10316     for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10317     PetscCall(DMCreateMatrix(dmc, mass));
10318     PetscCall(DMGetLocalVector(dmc, &u));
10319     PetscCall(DMPlexGetDepth(dmc, &depth));
10320     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
10321     PetscCall(MatZeroEntries(*mass));
10322     key.label = NULL;
10323     key.value = 0;
10324     key.field = 0;
10325     key.part  = 0;
10326     PetscCall(DMPlexComputeJacobianByKey(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
10327     PetscCall(ISDestroy(&cellIS));
10328     PetscCall(DMRestoreLocalVector(dmc, &u));
10329     PetscCall(DMDestroy(&dmc));
10330   } else {
10331     PetscCall(DMGetGlobalSection(dmFine, &gsf));
10332     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10333     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10334     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10335 
10336     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
10337     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10338     PetscCall(MatSetType(*mass, dmCoarse->mattype));
10339     PetscCall(DMGetApplicationContext(dmFine, &ctx));
10340 
10341     PetscCall(DMGetCoarseDM(dmFine, &cdm));
10342     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10343     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
10344     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
10345   }
10346   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
10347   PetscFunctionReturn(PETSC_SUCCESS);
10348 }
10349 
10350 /*@
10351   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10352 
10353   Input Parameter:
10354 . dm - The `DMPLEX` object
10355 
10356   Output Parameter:
10357 . regular - The flag
10358 
10359   Level: intermediate
10360 
10361 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
10362 @*/
10363 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
10364 {
10365   PetscFunctionBegin;
10366   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10367   PetscAssertPointer(regular, 2);
10368   *regular = ((DM_Plex *)dm->data)->regularRefinement;
10369   PetscFunctionReturn(PETSC_SUCCESS);
10370 }
10371 
10372 /*@
10373   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10374 
10375   Input Parameters:
10376 + dm      - The `DMPLEX` object
10377 - regular - The flag
10378 
10379   Level: intermediate
10380 
10381 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
10382 @*/
10383 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
10384 {
10385   PetscFunctionBegin;
10386   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10387   ((DM_Plex *)dm->data)->regularRefinement = regular;
10388   PetscFunctionReturn(PETSC_SUCCESS);
10389 }
10390 
10391 /*@
10392   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
10393   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
10394 
10395   Not Collective
10396 
10397   Input Parameter:
10398 . dm - The `DMPLEX` object
10399 
10400   Output Parameters:
10401 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
10402 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
10403 
10404   Level: intermediate
10405 
10406 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
10407 @*/
10408 PetscErrorCode DMPlexGetAnchors(DM dm, PeOp PetscSection *anchorSection, PeOp IS *anchorIS)
10409 {
10410   DM_Plex *plex = (DM_Plex *)dm->data;
10411 
10412   PetscFunctionBegin;
10413   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10414   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
10415   if (anchorSection) *anchorSection = plex->anchorSection;
10416   if (anchorIS) *anchorIS = plex->anchorIS;
10417   PetscFunctionReturn(PETSC_SUCCESS);
10418 }
10419 
10420 /*@
10421   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
10422 
10423   Collective
10424 
10425   Input Parameters:
10426 + dm            - The `DMPLEX` object
10427 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
10428                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10429 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10430 
10431   Level: intermediate
10432 
10433   Notes:
10434   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
10435   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
10436   combination of other points' degrees of freedom.
10437 
10438   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
10439   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
10440 
10441   The reference counts of `anchorSection` and `anchorIS` are incremented.
10442 
10443 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
10444 @*/
10445 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
10446 {
10447   DM_Plex    *plex = (DM_Plex *)dm->data;
10448   PetscMPIInt result;
10449 
10450   PetscFunctionBegin;
10451   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10452   if (anchorSection) {
10453     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
10454     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
10455     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
10456   }
10457   if (anchorIS) {
10458     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
10459     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
10460     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
10461   }
10462 
10463   PetscCall(PetscObjectReference((PetscObject)anchorSection));
10464   PetscCall(PetscSectionDestroy(&plex->anchorSection));
10465   plex->anchorSection = anchorSection;
10466 
10467   PetscCall(PetscObjectReference((PetscObject)anchorIS));
10468   PetscCall(ISDestroy(&plex->anchorIS));
10469   plex->anchorIS = anchorIS;
10470 
10471   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
10472     PetscInt        size, a, pStart, pEnd;
10473     const PetscInt *anchors;
10474 
10475     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10476     PetscCall(ISGetLocalSize(anchorIS, &size));
10477     PetscCall(ISGetIndices(anchorIS, &anchors));
10478     for (a = 0; a < size; a++) {
10479       PetscInt p;
10480 
10481       p = anchors[a];
10482       if (p >= pStart && p < pEnd) {
10483         PetscInt dof;
10484 
10485         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10486         if (dof) {
10487           PetscCall(ISRestoreIndices(anchorIS, &anchors));
10488           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
10489         }
10490       }
10491     }
10492     PetscCall(ISRestoreIndices(anchorIS, &anchors));
10493   }
10494   /* reset the generic constraints */
10495   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
10496   PetscFunctionReturn(PETSC_SUCCESS);
10497 }
10498 
10499 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
10500 {
10501   PetscSection anchorSection;
10502   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
10503 
10504   PetscFunctionBegin;
10505   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10506   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10507   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
10508   PetscCall(PetscSectionGetNumFields(section, &numFields));
10509   if (numFields) {
10510     PetscInt f;
10511     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
10512 
10513     for (f = 0; f < numFields; f++) {
10514       PetscInt numComp;
10515 
10516       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
10517       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
10518     }
10519   }
10520   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10521   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10522   pStart = PetscMax(pStart, sStart);
10523   pEnd   = PetscMin(pEnd, sEnd);
10524   pEnd   = PetscMax(pStart, pEnd);
10525   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
10526   for (p = pStart; p < pEnd; p++) {
10527     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10528     if (dof) {
10529       PetscCall(PetscSectionGetDof(section, p, &dof));
10530       PetscCall(PetscSectionSetDof(*cSec, p, dof));
10531       for (f = 0; f < numFields; f++) {
10532         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
10533         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
10534       }
10535     }
10536   }
10537   PetscCall(PetscSectionSetUp(*cSec));
10538   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
10539   PetscFunctionReturn(PETSC_SUCCESS);
10540 }
10541 
10542 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
10543 {
10544   PetscSection    aSec;
10545   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
10546   const PetscInt *anchors;
10547   PetscInt        numFields, f;
10548   IS              aIS;
10549   MatType         mtype;
10550   PetscBool       iscuda, iskokkos;
10551 
10552   PetscFunctionBegin;
10553   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10554   PetscCall(PetscSectionGetStorageSize(cSec, &m));
10555   PetscCall(PetscSectionGetStorageSize(section, &n));
10556   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
10557   PetscCall(MatSetSizes(*cMat, m, n, m, n));
10558   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
10559   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
10560   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
10561   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
10562   if (iscuda) mtype = MATSEQAIJCUSPARSE;
10563   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
10564   else mtype = MATSEQAIJ;
10565   PetscCall(MatSetType(*cMat, mtype));
10566   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
10567   PetscCall(ISGetIndices(aIS, &anchors));
10568   /* cSec will be a subset of aSec and section */
10569   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
10570   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10571   PetscCall(PetscMalloc1(m + 1, &i));
10572   i[0] = 0;
10573   PetscCall(PetscSectionGetNumFields(section, &numFields));
10574   for (p = pStart; p < pEnd; p++) {
10575     PetscInt rDof, rOff, r;
10576 
10577     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10578     if (!rDof) continue;
10579     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10580     if (numFields) {
10581       for (f = 0; f < numFields; f++) {
10582         annz = 0;
10583         for (r = 0; r < rDof; r++) {
10584           a = anchors[rOff + r];
10585           if (a < sStart || a >= sEnd) continue;
10586           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10587           annz += aDof;
10588         }
10589         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10590         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10591         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10592       }
10593     } else {
10594       annz = 0;
10595       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10596       for (q = 0; q < dof; q++) {
10597         a = anchors[rOff + q];
10598         if (a < sStart || a >= sEnd) continue;
10599         PetscCall(PetscSectionGetDof(section, a, &aDof));
10600         annz += aDof;
10601       }
10602       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10603       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10604       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10605     }
10606   }
10607   nnz = i[m];
10608   PetscCall(PetscMalloc1(nnz, &j));
10609   offset = 0;
10610   for (p = pStart; p < pEnd; p++) {
10611     if (numFields) {
10612       for (f = 0; f < numFields; f++) {
10613         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10614         for (q = 0; q < dof; q++) {
10615           PetscInt rDof, rOff, r;
10616           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10617           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10618           for (r = 0; r < rDof; r++) {
10619             PetscInt s;
10620 
10621             a = anchors[rOff + r];
10622             if (a < sStart || a >= sEnd) continue;
10623             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10624             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10625             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10626           }
10627         }
10628       }
10629     } else {
10630       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10631       for (q = 0; q < dof; q++) {
10632         PetscInt rDof, rOff, r;
10633         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10634         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10635         for (r = 0; r < rDof; r++) {
10636           PetscInt s;
10637 
10638           a = anchors[rOff + r];
10639           if (a < sStart || a >= sEnd) continue;
10640           PetscCall(PetscSectionGetDof(section, a, &aDof));
10641           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10642           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10643         }
10644       }
10645     }
10646   }
10647   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10648   PetscCall(PetscFree(i));
10649   PetscCall(PetscFree(j));
10650   PetscCall(ISRestoreIndices(aIS, &anchors));
10651   PetscFunctionReturn(PETSC_SUCCESS);
10652 }
10653 
10654 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10655 {
10656   DM_Plex     *plex = (DM_Plex *)dm->data;
10657   PetscSection anchorSection, section, cSec;
10658   Mat          cMat;
10659 
10660   PetscFunctionBegin;
10661   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10662   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10663   if (anchorSection) {
10664     PetscInt Nf;
10665 
10666     PetscCall(DMGetLocalSection(dm, &section));
10667     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10668     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10669     PetscCall(DMGetNumFields(dm, &Nf));
10670     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10671     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10672     PetscCall(PetscSectionDestroy(&cSec));
10673     PetscCall(MatDestroy(&cMat));
10674   }
10675   PetscFunctionReturn(PETSC_SUCCESS);
10676 }
10677 
10678 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10679 {
10680   IS           subis;
10681   PetscSection section, subsection;
10682 
10683   PetscFunctionBegin;
10684   PetscCall(DMGetLocalSection(dm, &section));
10685   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10686   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10687   /* Create subdomain */
10688   PetscCall(DMPlexFilter(dm, label, value, PETSC_FALSE, PETSC_FALSE, NULL, subdm));
10689   /* Create submodel */
10690   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10691   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10692   PetscCall(DMSetLocalSection(*subdm, subsection));
10693   PetscCall(PetscSectionDestroy(&subsection));
10694   PetscCall(DMCopyDisc(dm, *subdm));
10695   /* Create map from submodel to global model */
10696   if (is) {
10697     PetscSection    sectionGlobal, subsectionGlobal;
10698     IS              spIS;
10699     const PetscInt *spmap;
10700     PetscInt       *subIndices;
10701     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10702     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10703 
10704     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10705     PetscCall(ISGetIndices(spIS, &spmap));
10706     PetscCall(PetscSectionGetNumFields(section, &Nf));
10707     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10708     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10709     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10710     for (p = pStart; p < pEnd; ++p) {
10711       PetscInt gdof, pSubSize = 0;
10712 
10713       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10714       if (gdof > 0) {
10715         for (f = 0; f < Nf; ++f) {
10716           PetscInt fdof, fcdof;
10717 
10718           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10719           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10720           pSubSize += fdof - fcdof;
10721         }
10722         subSize += pSubSize;
10723         if (pSubSize) {
10724           if (bs < 0) {
10725             bs = pSubSize;
10726           } else if (bs != pSubSize) {
10727             /* Layout does not admit a pointwise block size */
10728             bs = 1;
10729           }
10730         }
10731       }
10732     }
10733     /* Must have same blocksize on all procs (some might have no points) */
10734     bsLocal[0] = bs < 0 ? PETSC_INT_MAX : bs;
10735     bsLocal[1] = bs;
10736     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10737     if (bsMinMax[0] != bsMinMax[1]) {
10738       bs = 1;
10739     } else {
10740       bs = bsMinMax[0];
10741     }
10742     PetscCall(PetscMalloc1(subSize, &subIndices));
10743     for (p = pStart; p < pEnd; ++p) {
10744       PetscInt gdof, goff;
10745 
10746       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10747       if (gdof > 0) {
10748         const PetscInt point = spmap[p];
10749 
10750         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10751         for (f = 0; f < Nf; ++f) {
10752           PetscInt fdof, fcdof, fc, f2, poff = 0;
10753 
10754           /* Can get rid of this loop by storing field information in the global section */
10755           for (f2 = 0; f2 < f; ++f2) {
10756             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10757             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10758             poff += fdof - fcdof;
10759           }
10760           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10761           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10762           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10763         }
10764       }
10765     }
10766     PetscCall(ISRestoreIndices(spIS, &spmap));
10767     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10768     if (bs > 1) {
10769       /* We need to check that the block size does not come from non-contiguous fields */
10770       PetscInt i, j, set = 1;
10771       for (i = 0; i < subSize; i += bs) {
10772         for (j = 0; j < bs; ++j) {
10773           if (subIndices[i + j] != subIndices[i] + j) {
10774             set = 0;
10775             break;
10776           }
10777         }
10778       }
10779       if (set) PetscCall(ISSetBlockSize(*is, bs));
10780     }
10781     /* Attach nullspace */
10782     for (f = 0; f < Nf; ++f) {
10783       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10784       if ((*subdm)->nullspaceConstructors[f]) break;
10785     }
10786     if (f < Nf) {
10787       MatNullSpace nullSpace;
10788       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10789 
10790       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10791       PetscCall(MatNullSpaceDestroy(&nullSpace));
10792     }
10793   }
10794   PetscFunctionReturn(PETSC_SUCCESS);
10795 }
10796 
10797 /*@
10798   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10799 
10800   Input Parameters:
10801 + dm    - The `DM`
10802 - dummy - unused argument
10803 
10804   Options Database Key:
10805 . -dm_plex_monitor_throughput - Activate the monitor
10806 
10807   Level: developer
10808 
10809 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10810 @*/
10811 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10812 {
10813   PetscLogHandler default_handler;
10814 
10815   PetscFunctionBegin;
10816   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10817   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10818   if (default_handler) {
10819     PetscLogEvent      event;
10820     PetscEventPerfInfo eventInfo;
10821     PetscLogDouble     cellRate, flopRate;
10822     PetscInt           cStart, cEnd, Nf, N;
10823     const char        *name;
10824 
10825     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10826     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10827     PetscCall(DMGetNumFields(dm, &Nf));
10828     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10829     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10830     N        = (cEnd - cStart) * Nf * eventInfo.count;
10831     flopRate = eventInfo.flops / eventInfo.time;
10832     cellRate = N / eventInfo.time;
10833     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));
10834   } else {
10835     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.");
10836   }
10837   PetscFunctionReturn(PETSC_SUCCESS);
10838 }
10839