xref: /petsc/src/dm/impls/plex/plex.c (revision a36b0f0f346e718409f8ecb84f382e8cd0966bc7)
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 lineColor, PetscInt cellColor, PetscInt cell, const PetscScalar coords[])
1826 {
1827   DMPolytopeType ct;
1828   PetscMPIInt    rank;
1829   PetscInt       cdim;
1830 
1831   PetscFunctionBegin;
1832   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1833   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1834   PetscCall(DMGetCoordinateDim(dm, &cdim));
1835   lineColor = lineColor < 0 ? PETSC_DRAW_BLACK : lineColor;
1836   cellColor = cellColor < 0 ? PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2 : cellColor;
1837   switch (ct) {
1838   case DM_POLYTOPE_SEGMENT:
1839   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1840     switch (cdim) {
1841     case 1: {
1842       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1843       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1844 
1845       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, lineColor));
1846       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, lineColor));
1847       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, lineColor));
1848     } break;
1849     case 2: {
1850       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1851       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1852       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1853 
1854       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), lineColor));
1855       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]) + l * dx, PetscRealPart(coords[1]) + l * dy, PetscRealPart(coords[0]) - l * dx, PetscRealPart(coords[1]) - l * dy, lineColor));
1856       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]) + l * dx, PetscRealPart(coords[3]) + l * dy, PetscRealPart(coords[2]) - l * dx, PetscRealPart(coords[3]) - l * dy, lineColor));
1857     } break;
1858     default:
1859       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1860     }
1861     break;
1862   case DM_POLYTOPE_TRIANGLE:
1863     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), cellColor, cellColor, cellColor));
1864     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), lineColor));
1865     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), lineColor));
1866     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), lineColor));
1867     break;
1868   case DM_POLYTOPE_QUADRILATERAL:
1869     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), cellColor, cellColor, cellColor));
1870     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), cellColor, cellColor, cellColor));
1871     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), lineColor));
1872     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), lineColor));
1873     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), lineColor));
1874     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), lineColor));
1875     break;
1876   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1877     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), cellColor, cellColor, cellColor));
1878     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), cellColor, cellColor, cellColor));
1879     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), lineColor));
1880     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), lineColor));
1881     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), lineColor));
1882     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), lineColor));
1883     break;
1884   case DM_POLYTOPE_FV_GHOST:
1885     break;
1886   default:
1887     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1888   }
1889   PetscFunctionReturn(PETSC_SUCCESS);
1890 }
1891 
1892 static PetscErrorCode DrawPolygon_Private(DM dm, PetscDraw draw, PetscInt cell, PetscInt Nv, const PetscReal refVertices[], const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1893 {
1894   PetscReal   centroid[2] = {0., 0.};
1895   PetscMPIInt rank;
1896   PetscMPIInt fillColor;
1897 
1898   PetscFunctionBegin;
1899   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1900   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1901   for (PetscInt v = 0; v < Nv; ++v) {
1902     centroid[0] += PetscRealPart(coords[v * 2 + 0]) / Nv;
1903     centroid[1] += PetscRealPart(coords[v * 2 + 1]) / Nv;
1904   }
1905   for (PetscInt e = 0; e < Nv; ++e) {
1906     refCoords[0] = refVertices[e * 2 + 0];
1907     refCoords[1] = refVertices[e * 2 + 1];
1908     for (PetscInt d = 1; d <= edgeDiv; ++d) {
1909       refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % Nv * 2 + 0] - refCoords[0]) * d / edgeDiv;
1910       refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % Nv * 2 + 1] - refCoords[1]) * d / edgeDiv;
1911     }
1912     PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1913     for (PetscInt d = 0; d < edgeDiv; ++d) {
1914       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));
1915       PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1916     }
1917   }
1918   PetscFunctionReturn(PETSC_SUCCESS);
1919 }
1920 
1921 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1922 {
1923   DMPolytopeType ct;
1924 
1925   PetscFunctionBegin;
1926   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1927   switch (ct) {
1928   case DM_POLYTOPE_TRIANGLE: {
1929     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1930 
1931     PetscCall(DrawPolygon_Private(dm, draw, cell, 3, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1932   } break;
1933   case DM_POLYTOPE_QUADRILATERAL: {
1934     PetscReal refVertices[8] = {-1., -1., 1., -1., 1., 1., -1., 1.};
1935 
1936     PetscCall(DrawPolygon_Private(dm, draw, cell, 4, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1937   } break;
1938   default:
1939     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1940   }
1941   PetscFunctionReturn(PETSC_SUCCESS);
1942 }
1943 
1944 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1945 {
1946   PetscDraw    draw;
1947   DM           cdm;
1948   PetscSection coordSection;
1949   Vec          coordinates;
1950   PetscReal    xyl[3], xyr[3];
1951   PetscReal   *refCoords, *edgeCoords;
1952   PetscBool    isnull, drawAffine;
1953   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, cDegree, edgeDiv, lineColor = PETSC_DETERMINE, cellColor = PETSC_DETERMINE;
1954 
1955   PetscFunctionBegin;
1956   PetscCall(DMGetCoordinateDim(dm, &dim));
1957   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1958   PetscCall(DMGetCoordinateDegree_Internal(dm, &cDegree));
1959   drawAffine = cDegree > 1 ? PETSC_FALSE : PETSC_TRUE;
1960   edgeDiv    = cDegree + 1;
1961   PetscCall(PetscOptionsGetInt(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_line_color", &lineColor, NULL));
1962   PetscCall(PetscOptionsGetInt(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_cell_color", &cellColor, NULL));
1963   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1964   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1965   PetscCall(DMGetCoordinateDM(dm, &cdm));
1966   PetscCall(DMGetLocalSection(cdm, &coordSection));
1967   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1968   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1969   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1970 
1971   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1972   PetscCall(PetscDrawIsNull(draw, &isnull));
1973   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1974   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1975 
1976   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1977   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1978   PetscCall(PetscDrawClear(draw));
1979 
1980   for (c = cStart; c < cEnd; ++c) {
1981     PetscScalar       *coords = NULL;
1982     const PetscScalar *coords_arr;
1983     PetscInt           numCoords;
1984     PetscBool          isDG;
1985 
1986     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1987     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, lineColor, cellColor, c, coords));
1988     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1989     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1990   }
1991   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1992   PetscCall(PetscDrawFlush(draw));
1993   PetscCall(PetscDrawPause(draw));
1994   PetscCall(PetscDrawSave(draw));
1995   PetscFunctionReturn(PETSC_SUCCESS);
1996 }
1997 
1998 static PetscErrorCode DMPlexCreateHighOrderSurrogate_Internal(DM dm, DM *hdm)
1999 {
2000   DM           odm = dm, rdm = dm, cdm;
2001   PetscFE      fe;
2002   PetscSpace   sp;
2003   PetscClassId id;
2004   PetscInt     degree;
2005   PetscBool    hoView = PETSC_TRUE;
2006 
2007   PetscFunctionBegin;
2008   PetscObjectOptionsBegin((PetscObject)dm);
2009   PetscCall(PetscOptionsBool("-dm_plex_high_order_view", "Subsample to view meshes with high order coordinates", "DMPlexCreateHighOrderSurrogate_Internal", hoView, &hoView, NULL));
2010   PetscOptionsEnd();
2011   PetscCall(PetscObjectReference((PetscObject)dm));
2012   *hdm = dm;
2013   if (!hoView) PetscFunctionReturn(PETSC_SUCCESS);
2014   PetscCall(DMGetCoordinateDM(dm, &cdm));
2015   PetscCall(DMGetField(cdm, 0, NULL, (PetscObject *)&fe));
2016   PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
2017   if (id != PETSCFE_CLASSID) PetscFunctionReturn(PETSC_SUCCESS);
2018   PetscCall(PetscFEGetBasisSpace(fe, &sp));
2019   PetscCall(PetscSpaceGetDegree(sp, &degree, NULL));
2020   for (PetscInt r = 0, rd = PetscCeilReal(((PetscReal)degree) / 2.); r < (PetscInt)PetscCeilReal(PetscLog2Real(degree)); ++r, rd = PetscCeilReal(((PetscReal)rd) / 2.)) {
2021     DM  cdm, rcdm;
2022     Mat In;
2023     Vec cl, rcl;
2024 
2025     PetscCall(DMRefine(odm, PetscObjectComm((PetscObject)odm), &rdm));
2026     PetscCall(DMPlexCreateCoordinateSpace(rdm, rd, PETSC_FALSE, NULL));
2027     PetscCall(PetscObjectSetName((PetscObject)rdm, "Refined Mesh with Linear Coordinates"));
2028     PetscCall(DMGetCoordinateDM(odm, &cdm));
2029     PetscCall(DMGetCoordinateDM(rdm, &rcdm));
2030     PetscCall(DMGetCoordinatesLocal(odm, &cl));
2031     PetscCall(DMGetCoordinatesLocal(rdm, &rcl));
2032     PetscCall(DMSetCoarseDM(rcdm, cdm));
2033     PetscCall(DMCreateInterpolation(cdm, rcdm, &In, NULL));
2034     PetscCall(MatMult(In, cl, rcl));
2035     PetscCall(MatDestroy(&In));
2036     PetscCall(DMSetCoordinatesLocal(rdm, rcl));
2037     PetscCall(DMDestroy(&odm));
2038     odm = rdm;
2039   }
2040   *hdm = rdm;
2041   PetscFunctionReturn(PETSC_SUCCESS);
2042 }
2043 
2044 #if defined(PETSC_HAVE_EXODUSII)
2045   #include <exodusII.h>
2046   #include <petscviewerexodusii.h>
2047 #endif
2048 
2049 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
2050 {
2051   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns, ispython;
2052   char      name[PETSC_MAX_PATH_LEN];
2053 
2054   PetscFunctionBegin;
2055   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2056   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2057   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
2058   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
2059   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2060   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
2061   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
2062   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
2063   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
2064   PetscCall(PetscObjectHasFunction((PetscObject)viewer, "PetscViewerPythonViewObject_C", &ispython));
2065   if (iascii) {
2066     PetscViewerFormat format;
2067     PetscCall(PetscViewerGetFormat(viewer, &format));
2068     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
2069     else PetscCall(DMPlexView_Ascii(dm, viewer));
2070   } else if (ishdf5) {
2071 #if defined(PETSC_HAVE_HDF5)
2072     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
2073 #else
2074     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2075 #endif
2076   } else if (isvtk) {
2077     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
2078   } else if (isdraw) {
2079     DM hdm;
2080 
2081     PetscCall(DMPlexCreateHighOrderSurrogate_Internal(dm, &hdm));
2082     PetscCall(DMPlexView_Draw(hdm, viewer));
2083     PetscCall(DMDestroy(&hdm));
2084   } else if (isglvis) {
2085     PetscCall(DMPlexView_GLVis(dm, viewer));
2086 #if defined(PETSC_HAVE_EXODUSII)
2087   } else if (isexodus) {
2088     /*
2089       exodusII requires that all sets be part of exactly one cell set.
2090       If the dm does not have a "Cell Sets" label defined, we create one
2091       with ID 1, containing all cells.
2092       Note that if the Cell Sets label is defined but does not cover all cells,
2093       we may still have a problem. This should probably be checked here or in the viewer;
2094     */
2095     PetscInt numCS;
2096     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
2097     if (!numCS) {
2098       PetscInt cStart, cEnd, c;
2099       PetscCall(DMCreateLabel(dm, "Cell Sets"));
2100       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
2101       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
2102     }
2103     PetscCall(DMView_PlexExodusII(dm, viewer));
2104 #endif
2105 #if defined(PETSC_HAVE_CGNS)
2106   } else if (iscgns) {
2107     PetscCall(DMView_PlexCGNS(dm, viewer));
2108 #endif
2109   } else if (ispython) {
2110     PetscCall(PetscViewerPythonViewObject(viewer, (PetscObject)dm));
2111   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
2112   /* Optionally view the partition */
2113   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
2114   if (flg) {
2115     Vec ranks;
2116     PetscCall(DMPlexCreateRankField(dm, &ranks));
2117     PetscCall(VecView(ranks, viewer));
2118     PetscCall(VecDestroy(&ranks));
2119   }
2120   /* Optionally view a label */
2121   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
2122   if (flg) {
2123     DMLabel label;
2124     Vec     val;
2125 
2126     PetscCall(DMGetLabel(dm, name, &label));
2127     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
2128     PetscCall(DMPlexCreateLabelField(dm, label, &val));
2129     PetscCall(VecView(val, viewer));
2130     PetscCall(VecDestroy(&val));
2131   }
2132   PetscFunctionReturn(PETSC_SUCCESS);
2133 }
2134 
2135 /*@
2136   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
2137 
2138   Collective
2139 
2140   Input Parameters:
2141 + dm     - The `DM` whose topology is to be saved
2142 - viewer - The `PetscViewer` to save it in
2143 
2144   Level: advanced
2145 
2146 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
2147 @*/
2148 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
2149 {
2150   PetscBool ishdf5;
2151 
2152   PetscFunctionBegin;
2153   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2154   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2155   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2156   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
2157   if (ishdf5) {
2158 #if defined(PETSC_HAVE_HDF5)
2159     PetscViewerFormat format;
2160     PetscCall(PetscViewerGetFormat(viewer, &format));
2161     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2162       IS globalPointNumbering;
2163 
2164       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2165       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
2166       PetscCall(ISDestroy(&globalPointNumbering));
2167     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2168 #else
2169     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2170 #endif
2171   }
2172   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
2173   PetscFunctionReturn(PETSC_SUCCESS);
2174 }
2175 
2176 /*@
2177   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
2178 
2179   Collective
2180 
2181   Input Parameters:
2182 + dm     - The `DM` whose coordinates are to be saved
2183 - viewer - The `PetscViewer` for saving
2184 
2185   Level: advanced
2186 
2187 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
2188 @*/
2189 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
2190 {
2191   PetscBool ishdf5;
2192 
2193   PetscFunctionBegin;
2194   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2195   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2196   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2197   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2198   if (ishdf5) {
2199 #if defined(PETSC_HAVE_HDF5)
2200     PetscViewerFormat format;
2201     PetscCall(PetscViewerGetFormat(viewer, &format));
2202     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2203       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
2204     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2205 #else
2206     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2207 #endif
2208   }
2209   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2210   PetscFunctionReturn(PETSC_SUCCESS);
2211 }
2212 
2213 /*@
2214   DMPlexLabelsView - Saves `DMPLEX` labels into a file
2215 
2216   Collective
2217 
2218   Input Parameters:
2219 + dm     - The `DM` whose labels are to be saved
2220 - viewer - The `PetscViewer` for saving
2221 
2222   Level: advanced
2223 
2224 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
2225 @*/
2226 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
2227 {
2228   PetscBool ishdf5;
2229 
2230   PetscFunctionBegin;
2231   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2232   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2233   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2234   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
2235   if (ishdf5) {
2236 #if defined(PETSC_HAVE_HDF5)
2237     IS                globalPointNumbering;
2238     PetscViewerFormat format;
2239 
2240     PetscCall(PetscViewerGetFormat(viewer, &format));
2241     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2242       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2243       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
2244       PetscCall(ISDestroy(&globalPointNumbering));
2245     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2246 #else
2247     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2248 #endif
2249   }
2250   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
2251   PetscFunctionReturn(PETSC_SUCCESS);
2252 }
2253 
2254 /*@
2255   DMPlexSectionView - Saves a section associated with a `DMPLEX`
2256 
2257   Collective
2258 
2259   Input Parameters:
2260 + dm        - The `DM` that contains the topology on which the section to be saved is defined
2261 . viewer    - The `PetscViewer` for saving
2262 - sectiondm - The `DM` that contains the section to be saved, can be `NULL`
2263 
2264   Level: advanced
2265 
2266   Notes:
2267   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.
2268 
2269   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.
2270 
2271 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
2272 @*/
2273 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
2274 {
2275   PetscBool ishdf5;
2276 
2277   PetscFunctionBegin;
2278   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2279   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2280   if (!sectiondm) sectiondm = dm;
2281   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2282   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2283   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
2284   if (ishdf5) {
2285 #if defined(PETSC_HAVE_HDF5)
2286     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
2287 #else
2288     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2289 #endif
2290   }
2291   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
2292   PetscFunctionReturn(PETSC_SUCCESS);
2293 }
2294 
2295 /*@
2296   DMPlexGlobalVectorView - Saves a global vector
2297 
2298   Collective
2299 
2300   Input Parameters:
2301 + dm        - The `DM` that represents the topology
2302 . viewer    - The `PetscViewer` to save data with
2303 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2304 - vec       - The global vector to be saved
2305 
2306   Level: advanced
2307 
2308   Notes:
2309   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.
2310 
2311   Calling sequence:
2312 .vb
2313        DMCreate(PETSC_COMM_WORLD, &dm);
2314        DMSetType(dm, DMPLEX);
2315        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2316        DMClone(dm, &sectiondm);
2317        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2318        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2319        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2320        PetscSectionSetChart(section, pStart, pEnd);
2321        PetscSectionSetUp(section);
2322        DMSetLocalSection(sectiondm, section);
2323        PetscSectionDestroy(&section);
2324        DMGetGlobalVector(sectiondm, &vec);
2325        PetscObjectSetName((PetscObject)vec, "vec_name");
2326        DMPlexTopologyView(dm, viewer);
2327        DMPlexSectionView(dm, viewer, sectiondm);
2328        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2329        DMRestoreGlobalVector(sectiondm, &vec);
2330        DMDestroy(&sectiondm);
2331        DMDestroy(&dm);
2332 .ve
2333 
2334 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2335 @*/
2336 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2337 {
2338   PetscBool ishdf5;
2339 
2340   PetscFunctionBegin;
2341   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2342   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2343   if (!sectiondm) sectiondm = dm;
2344   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2345   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2346   /* Check consistency */
2347   {
2348     PetscSection section;
2349     PetscBool    includesConstraints;
2350     PetscInt     m, m1;
2351 
2352     PetscCall(VecGetLocalSize(vec, &m1));
2353     PetscCall(DMGetGlobalSection(sectiondm, &section));
2354     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2355     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2356     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2357     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2358   }
2359   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2360   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2361   if (ishdf5) {
2362 #if defined(PETSC_HAVE_HDF5)
2363     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2364 #else
2365     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2366 #endif
2367   }
2368   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2369   PetscFunctionReturn(PETSC_SUCCESS);
2370 }
2371 
2372 /*@
2373   DMPlexLocalVectorView - Saves a local vector
2374 
2375   Collective
2376 
2377   Input Parameters:
2378 + dm        - The `DM` that represents the topology
2379 . viewer    - The `PetscViewer` to save data with
2380 . sectiondm - The `DM` that contains the local section on which `vec` is defined, can be `NULL`
2381 - vec       - The local vector to be saved
2382 
2383   Level: advanced
2384 
2385   Note:
2386   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.
2387 
2388   Calling sequence:
2389 .vb
2390        DMCreate(PETSC_COMM_WORLD, &dm);
2391        DMSetType(dm, DMPLEX);
2392        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2393        DMClone(dm, &sectiondm);
2394        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2395        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2396        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2397        PetscSectionSetChart(section, pStart, pEnd);
2398        PetscSectionSetUp(section);
2399        DMSetLocalSection(sectiondm, section);
2400        DMGetLocalVector(sectiondm, &vec);
2401        PetscObjectSetName((PetscObject)vec, "vec_name");
2402        DMPlexTopologyView(dm, viewer);
2403        DMPlexSectionView(dm, viewer, sectiondm);
2404        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2405        DMRestoreLocalVector(sectiondm, &vec);
2406        DMDestroy(&sectiondm);
2407        DMDestroy(&dm);
2408 .ve
2409 
2410 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2411 @*/
2412 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2413 {
2414   PetscBool ishdf5;
2415 
2416   PetscFunctionBegin;
2417   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2418   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2419   if (!sectiondm) sectiondm = dm;
2420   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2421   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2422   /* Check consistency */
2423   {
2424     PetscSection section;
2425     PetscBool    includesConstraints;
2426     PetscInt     m, m1;
2427 
2428     PetscCall(VecGetLocalSize(vec, &m1));
2429     PetscCall(DMGetLocalSection(sectiondm, &section));
2430     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2431     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2432     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2433     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2434   }
2435   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2436   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2437   if (ishdf5) {
2438 #if defined(PETSC_HAVE_HDF5)
2439     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2440 #else
2441     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2442 #endif
2443   }
2444   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2445   PetscFunctionReturn(PETSC_SUCCESS);
2446 }
2447 
2448 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2449 {
2450   PetscBool ishdf5;
2451 
2452   PetscFunctionBegin;
2453   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2454   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2455   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2456   if (ishdf5) {
2457 #if defined(PETSC_HAVE_HDF5)
2458     PetscViewerFormat format;
2459     PetscCall(PetscViewerGetFormat(viewer, &format));
2460     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2461       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2462     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2463       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2464     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2465     PetscFunctionReturn(PETSC_SUCCESS);
2466 #else
2467     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2468 #endif
2469   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2470 }
2471 
2472 /*@
2473   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2474 
2475   Collective
2476 
2477   Input Parameters:
2478 + dm     - The `DM` into which the topology is loaded
2479 - viewer - The `PetscViewer` for the saved topology
2480 
2481   Output Parameter:
2482 . 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;
2483   `NULL` if unneeded
2484 
2485   Level: advanced
2486 
2487 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2488           `PetscViewer`, `PetscSF`
2489 @*/
2490 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2491 {
2492   PetscBool ishdf5;
2493 
2494   PetscFunctionBegin;
2495   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2496   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2497   if (globalToLocalPointSF) PetscAssertPointer(globalToLocalPointSF, 3);
2498   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2499   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2500   if (ishdf5) {
2501 #if defined(PETSC_HAVE_HDF5)
2502     PetscViewerFormat format;
2503     PetscCall(PetscViewerGetFormat(viewer, &format));
2504     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2505       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2506     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2507 #else
2508     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2509 #endif
2510   }
2511   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2512   PetscFunctionReturn(PETSC_SUCCESS);
2513 }
2514 
2515 /*@
2516   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2517 
2518   Collective
2519 
2520   Input Parameters:
2521 + dm                   - The `DM` into which the coordinates are loaded
2522 . viewer               - The `PetscViewer` for the saved coordinates
2523 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2524 
2525   Level: advanced
2526 
2527 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2528           `PetscSF`, `PetscViewer`
2529 @*/
2530 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2531 {
2532   PetscBool ishdf5;
2533 
2534   PetscFunctionBegin;
2535   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2536   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2537   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2538   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2539   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2540   if (ishdf5) {
2541 #if defined(PETSC_HAVE_HDF5)
2542     PetscViewerFormat format;
2543     PetscCall(PetscViewerGetFormat(viewer, &format));
2544     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2545       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2546     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2547 #else
2548     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2549 #endif
2550   }
2551   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2552   PetscFunctionReturn(PETSC_SUCCESS);
2553 }
2554 
2555 /*@
2556   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2557 
2558   Collective
2559 
2560   Input Parameters:
2561 + dm                   - The `DM` into which the labels are loaded
2562 . viewer               - The `PetscViewer` for the saved labels
2563 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2564 
2565   Level: advanced
2566 
2567   Note:
2568   The `PetscSF` argument must not be `NULL` if the `DM` is distributed, otherwise an error occurs.
2569 
2570 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2571           `PetscSF`, `PetscViewer`
2572 @*/
2573 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2574 {
2575   PetscBool ishdf5;
2576 
2577   PetscFunctionBegin;
2578   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2579   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2580   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2581   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2582   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2583   if (ishdf5) {
2584 #if defined(PETSC_HAVE_HDF5)
2585     PetscViewerFormat format;
2586 
2587     PetscCall(PetscViewerGetFormat(viewer, &format));
2588     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2589       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2590     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2591 #else
2592     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2593 #endif
2594   }
2595   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2596   PetscFunctionReturn(PETSC_SUCCESS);
2597 }
2598 
2599 /*@
2600   DMPlexSectionLoad - Loads section into a `DMPLEX`
2601 
2602   Collective
2603 
2604   Input Parameters:
2605 + dm                   - The `DM` that represents the topology
2606 . viewer               - The `PetscViewer` that represents the on-disk section (sectionA)
2607 . sectiondm            - The `DM` into which the on-disk section (sectionA) is migrated, can be `NULL`
2608 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2609 
2610   Output Parameters:
2611 + 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)
2612 - 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)
2613 
2614   Level: advanced
2615 
2616   Notes:
2617   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.
2618 
2619   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.
2620 
2621   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.
2622 
2623   Example using 2 processes:
2624 .vb
2625   NX (number of points on dm): 4
2626   sectionA                   : the on-disk section
2627   vecA                       : a vector associated with sectionA
2628   sectionB                   : sectiondm's local section constructed in this function
2629   vecB (local)               : a vector associated with sectiondm's local section
2630   vecB (global)              : a vector associated with sectiondm's global section
2631 
2632                                      rank 0    rank 1
2633   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2634   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2635   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2636   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2637   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2638   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2639   sectionB->atlasDof             :     1 0 1 | 1 3
2640   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2641   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2642   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2643 .ve
2644   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2645 
2646 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2647 @*/
2648 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, PeOp DM sectiondm, PetscSF globalToLocalPointSF, PeOp PetscSF *globalDofSF, PeOp PetscSF *localDofSF)
2649 {
2650   PetscBool ishdf5;
2651 
2652   PetscFunctionBegin;
2653   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2654   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2655   if (!sectiondm) sectiondm = dm;
2656   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2657   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2658   if (globalDofSF) PetscAssertPointer(globalDofSF, 5);
2659   if (localDofSF) PetscAssertPointer(localDofSF, 6);
2660   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2661   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2662   if (ishdf5) {
2663 #if defined(PETSC_HAVE_HDF5)
2664     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2665 #else
2666     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2667 #endif
2668   }
2669   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2670   PetscFunctionReturn(PETSC_SUCCESS);
2671 }
2672 
2673 /*@
2674   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2675 
2676   Collective
2677 
2678   Input Parameters:
2679 + dm        - The `DM` that represents the topology
2680 . viewer    - The `PetscViewer` that represents the on-disk vector data
2681 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2682 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2683 - vec       - The global vector to set values of
2684 
2685   Level: advanced
2686 
2687   Notes:
2688   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2689 
2690   Calling sequence:
2691 .vb
2692        DMCreate(PETSC_COMM_WORLD, &dm);
2693        DMSetType(dm, DMPLEX);
2694        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2695        DMPlexTopologyLoad(dm, viewer, &sfX);
2696        DMClone(dm, &sectiondm);
2697        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2698        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2699        DMGetGlobalVector(sectiondm, &vec);
2700        PetscObjectSetName((PetscObject)vec, "vec_name");
2701        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2702        DMRestoreGlobalVector(sectiondm, &vec);
2703        PetscSFDestroy(&gsf);
2704        PetscSFDestroy(&sfX);
2705        DMDestroy(&sectiondm);
2706        DMDestroy(&dm);
2707 .ve
2708 
2709 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2710           `PetscSF`, `PetscViewer`
2711 @*/
2712 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2713 {
2714   PetscBool ishdf5;
2715 
2716   PetscFunctionBegin;
2717   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2718   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2719   if (!sectiondm) sectiondm = dm;
2720   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2721   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2722   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2723   /* Check consistency */
2724   {
2725     PetscSection section;
2726     PetscBool    includesConstraints;
2727     PetscInt     m, m1;
2728 
2729     PetscCall(VecGetLocalSize(vec, &m1));
2730     PetscCall(DMGetGlobalSection(sectiondm, &section));
2731     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2732     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2733     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2734     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2735   }
2736   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2737   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2738   if (ishdf5) {
2739 #if defined(PETSC_HAVE_HDF5)
2740     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2741 #else
2742     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2743 #endif
2744   }
2745   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2746   PetscFunctionReturn(PETSC_SUCCESS);
2747 }
2748 
2749 /*@
2750   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2751 
2752   Collective
2753 
2754   Input Parameters:
2755 + dm        - The `DM` that represents the topology
2756 . viewer    - The `PetscViewer` that represents the on-disk vector data
2757 . sectiondm - The `DM` that contains the local section on which vec is defined, can be `NULL`
2758 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2759 - vec       - The local vector to set values of
2760 
2761   Level: advanced
2762 
2763   Notes:
2764   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.
2765 
2766   Calling sequence:
2767 .vb
2768        DMCreate(PETSC_COMM_WORLD, &dm);
2769        DMSetType(dm, DMPLEX);
2770        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2771        DMPlexTopologyLoad(dm, viewer, &sfX);
2772        DMClone(dm, &sectiondm);
2773        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2774        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2775        DMGetLocalVector(sectiondm, &vec);
2776        PetscObjectSetName((PetscObject)vec, "vec_name");
2777        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2778        DMRestoreLocalVector(sectiondm, &vec);
2779        PetscSFDestroy(&lsf);
2780        PetscSFDestroy(&sfX);
2781        DMDestroy(&sectiondm);
2782        DMDestroy(&dm);
2783 .ve
2784 
2785 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2786           `PetscSF`, `PetscViewer`
2787 @*/
2788 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2789 {
2790   PetscBool ishdf5;
2791 
2792   PetscFunctionBegin;
2793   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2794   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2795   if (!sectiondm) sectiondm = dm;
2796   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2797   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2798   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2799   /* Check consistency */
2800   {
2801     PetscSection section;
2802     PetscBool    includesConstraints;
2803     PetscInt     m, m1;
2804 
2805     PetscCall(VecGetLocalSize(vec, &m1));
2806     PetscCall(DMGetLocalSection(sectiondm, &section));
2807     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2808     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2809     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2810     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2811   }
2812   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2813   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2814   if (ishdf5) {
2815 #if defined(PETSC_HAVE_HDF5)
2816     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2817 #else
2818     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2819 #endif
2820   }
2821   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2822   PetscFunctionReturn(PETSC_SUCCESS);
2823 }
2824 
2825 PetscErrorCode DMDestroy_Plex(DM dm)
2826 {
2827   DM_Plex *mesh = (DM_Plex *)dm->data;
2828 
2829   PetscFunctionBegin;
2830   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2831   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2832   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", NULL));
2833   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBounds_C", NULL));
2834   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2835   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2836   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2837   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2838   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2839   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2840   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2841   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2842   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetDefault_C", NULL));
2843   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetDefault_C", NULL));
2844   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetType_C", NULL));
2845   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetType_C", NULL));
2846   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2847   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2848   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetUseCeed_C", NULL));
2849   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetUseCeed_C", NULL));
2850   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2851   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2852   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2853   PetscCall(PetscFree(mesh->cones));
2854   PetscCall(PetscFree(mesh->coneOrientations));
2855   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2856   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2857   PetscCall(PetscFree(mesh->supports));
2858   PetscCall(PetscFree(mesh->cellTypes));
2859   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2860   PetscCall(PetscFree(mesh->tetgenOpts));
2861   PetscCall(PetscFree(mesh->triangleOpts));
2862   PetscCall(PetscFree(mesh->transformType));
2863   PetscCall(PetscFree(mesh->distributionName));
2864   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2865   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2866   PetscCall(ISDestroy(&mesh->subpointIS));
2867   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2868   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2869   if (mesh->periodic.face_sfs) {
2870     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFDestroy(&mesh->periodic.face_sfs[i]));
2871     PetscCall(PetscFree(mesh->periodic.face_sfs));
2872   }
2873   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2874   if (mesh->periodic.periodic_points) {
2875     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(ISDestroy(&mesh->periodic.periodic_points[i]));
2876     PetscCall(PetscFree(mesh->periodic.periodic_points));
2877   }
2878   if (mesh->periodic.transform) PetscCall(PetscFree(mesh->periodic.transform));
2879   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2880   PetscCall(ISDestroy(&mesh->anchorIS));
2881   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2882   PetscCall(PetscFree(mesh->parents));
2883   PetscCall(PetscFree(mesh->childIDs));
2884   PetscCall(PetscSectionDestroy(&mesh->childSection));
2885   PetscCall(PetscFree(mesh->children));
2886   PetscCall(DMDestroy(&mesh->referenceTree));
2887   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2888   PetscCall(PetscFree(mesh->neighbors));
2889   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2890   if (mesh->nonempty_comm != MPI_COMM_NULL && mesh->nonempty_comm != MPI_COMM_SELF) PetscCallMPI(MPI_Comm_free(&mesh->nonempty_comm));
2891   PetscCall(DMPlexTransformDestroy(&mesh->transform));
2892   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2893   PetscCall(PetscFree(mesh));
2894   PetscFunctionReturn(PETSC_SUCCESS);
2895 }
2896 
2897 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2898 {
2899   PetscSection           sectionGlobal, sectionLocal;
2900   PetscInt               bs = -1, mbs;
2901   PetscInt               localSize, localStart = 0;
2902   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2903   MatType                mtype;
2904   ISLocalToGlobalMapping ltog;
2905 
2906   PetscFunctionBegin;
2907   PetscCall(MatInitializePackage());
2908   mtype = dm->mattype;
2909   PetscCall(DMGetLocalSection(dm, &sectionLocal));
2910   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2911   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2912   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2913   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2914   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2915   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2916   PetscCall(MatSetType(*J, mtype));
2917   PetscCall(MatSetFromOptions(*J));
2918   PetscCall(MatGetBlockSize(*J, &mbs));
2919   if (mbs > 1) bs = mbs;
2920   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2921   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2922   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2923   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2924   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2925   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2926   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2927   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2928   if (!isShell) {
2929     // There are three states with pblocks, since block starts can have no dofs:
2930     // UNKNOWN) New Block:   An open block has been signalled by pblocks[p] == 1
2931     // TRUE)    Block Start: The first entry in a block has been added
2932     // FALSE)   Block Add:   An additional block entry has been added, since pblocks[p] == 0
2933     PetscBT         blst;
2934     PetscBool3      bstate     = PETSC_BOOL3_UNKNOWN;
2935     PetscBool       fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2936     const PetscInt *perm       = NULL;
2937     PetscInt       *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2938     PetscInt        pStart, pEnd, dof, cdof, num_fields;
2939 
2940     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2941     PetscCall(PetscSectionGetBlockStarts(sectionLocal, &blst));
2942     if (sectionLocal->perm) PetscCall(ISGetIndices(sectionLocal->perm, &perm));
2943 
2944     PetscCall(PetscCalloc1(localSize, &pblocks));
2945     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2946     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2947     // We need to process in the permuted order to get block sizes right
2948     for (PetscInt point = pStart; point < pEnd; ++point) {
2949       const PetscInt p = perm ? perm[point] : point;
2950 
2951       switch (dm->blocking_type) {
2952       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2953         PetscInt bdof, offset;
2954 
2955         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2956         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2957         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2958         if (blst && PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_UNKNOWN;
2959         if (dof > 0) {
2960           // State change
2961           if (bstate == PETSC_BOOL3_UNKNOWN) bstate = PETSC_BOOL3_TRUE;
2962           else if (bstate == PETSC_BOOL3_TRUE && blst && !PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_FALSE;
2963 
2964           for (PetscInt i = 0; i < dof - cdof; ++i) pblocks[offset - localStart + i] = dof - cdof;
2965           // Signal block concatenation
2966           if (bstate == PETSC_BOOL3_FALSE && dof - cdof) pblocks[offset - localStart] = -(dof - cdof);
2967         }
2968         dof  = dof < 0 ? -(dof + 1) : dof;
2969         bdof = cdof && (dof - cdof) ? 1 : dof;
2970         if (dof) {
2971           if (bs < 0) {
2972             bs = bdof;
2973           } else if (bs != bdof) {
2974             bs = 1;
2975           }
2976         }
2977       } break;
2978       case DM_BLOCKING_FIELD_NODE: {
2979         for (PetscInt field = 0; field < num_fields; field++) {
2980           PetscInt num_comp, bdof, offset;
2981           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2982           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2983           if (dof < 0) continue;
2984           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2985           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2986           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);
2987           PetscInt num_nodes = dof / num_comp;
2988           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2989           // Handle possibly constant block size (unlikely)
2990           bdof = cdof && (dof - cdof) ? 1 : dof;
2991           if (dof) {
2992             if (bs < 0) {
2993               bs = bdof;
2994             } else if (bs != bdof) {
2995               bs = 1;
2996             }
2997           }
2998         }
2999       } break;
3000       }
3001     }
3002     if (sectionLocal->perm) PetscCall(ISRestoreIndices(sectionLocal->perm, &perm));
3003     /* Must have same blocksize on all procs (some might have no points) */
3004     bsLocal[0] = bs < 0 ? PETSC_INT_MAX : bs;
3005     bsLocal[1] = bs;
3006     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
3007     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
3008     else bs = bsMinMax[0];
3009     bs = PetscMax(1, bs);
3010     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
3011     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
3012       PetscCall(MatSetBlockSize(*J, bs));
3013       PetscCall(MatSetUp(*J));
3014     } else {
3015       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
3016       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
3017       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
3018     }
3019     if (pblocks) { // Consolidate blocks
3020       PetscInt nblocks = 0;
3021       pblocks[0]       = PetscAbs(pblocks[0]);
3022       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
3023         if (pblocks[i] == 0) continue;
3024         // Negative block size indicates the blocks should be concatenated
3025         if (pblocks[i] < 0) {
3026           pblocks[i] = -pblocks[i];
3027           pblocks[nblocks - 1] += pblocks[i];
3028         } else {
3029           pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
3030         }
3031         for (PetscInt j = 1; j < pblocks[i]; j++)
3032           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);
3033       }
3034       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
3035     }
3036     PetscCall(PetscFree(pblocks));
3037   }
3038   PetscCall(MatSetDM(*J, dm));
3039   PetscFunctionReturn(PETSC_SUCCESS);
3040 }
3041 
3042 /*@
3043   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
3044 
3045   Not Collective
3046 
3047   Input Parameter:
3048 . dm - The `DMPLEX`
3049 
3050   Output Parameter:
3051 . subsection - The subdomain section
3052 
3053   Level: developer
3054 
3055 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
3056 @*/
3057 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
3058 {
3059   DM_Plex *mesh = (DM_Plex *)dm->data;
3060 
3061   PetscFunctionBegin;
3062   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3063   if (!mesh->subdomainSection) {
3064     PetscSection section;
3065     PetscSF      sf;
3066 
3067     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
3068     PetscCall(DMGetLocalSection(dm, &section));
3069     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
3070     PetscCall(PetscSFDestroy(&sf));
3071   }
3072   *subsection = mesh->subdomainSection;
3073   PetscFunctionReturn(PETSC_SUCCESS);
3074 }
3075 
3076 /*@
3077   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
3078 
3079   Not Collective
3080 
3081   Input Parameter:
3082 . dm - The `DMPLEX`
3083 
3084   Output Parameters:
3085 + pStart - The first mesh point
3086 - pEnd   - The upper bound for mesh points
3087 
3088   Level: beginner
3089 
3090 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
3091 @*/
3092 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
3093 {
3094   DM_Plex *mesh = (DM_Plex *)dm->data;
3095 
3096   PetscFunctionBegin;
3097   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3098   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
3099   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
3100   PetscFunctionReturn(PETSC_SUCCESS);
3101 }
3102 
3103 /*@
3104   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
3105 
3106   Not Collective
3107 
3108   Input Parameters:
3109 + dm     - The `DMPLEX`
3110 . pStart - The first mesh point
3111 - pEnd   - The upper bound for mesh points
3112 
3113   Level: beginner
3114 
3115 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
3116 @*/
3117 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
3118 {
3119   DM_Plex *mesh = (DM_Plex *)dm->data;
3120 
3121   PetscFunctionBegin;
3122   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3123   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
3124   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
3125   PetscCall(PetscFree(mesh->cellTypes));
3126   PetscFunctionReturn(PETSC_SUCCESS);
3127 }
3128 
3129 /*@
3130   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
3131 
3132   Not Collective
3133 
3134   Input Parameters:
3135 + dm - The `DMPLEX`
3136 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3137 
3138   Output Parameter:
3139 . size - The cone size for point `p`
3140 
3141   Level: beginner
3142 
3143 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3144 @*/
3145 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
3146 {
3147   DM_Plex *mesh = (DM_Plex *)dm->data;
3148 
3149   PetscFunctionBegin;
3150   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3151   PetscAssertPointer(size, 3);
3152   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
3153   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
3154   PetscFunctionReturn(PETSC_SUCCESS);
3155 }
3156 
3157 /*@
3158   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
3159 
3160   Not Collective
3161 
3162   Input Parameters:
3163 + dm   - The `DMPLEX`
3164 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3165 - size - The cone size for point `p`
3166 
3167   Level: beginner
3168 
3169   Note:
3170   This should be called after `DMPlexSetChart()`.
3171 
3172 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
3173 @*/
3174 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
3175 {
3176   DM_Plex *mesh = (DM_Plex *)dm->data;
3177 
3178   PetscFunctionBegin;
3179   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3180   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
3181   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
3182   PetscFunctionReturn(PETSC_SUCCESS);
3183 }
3184 
3185 /*@C
3186   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
3187 
3188   Not Collective
3189 
3190   Input Parameters:
3191 + dm - The `DMPLEX`
3192 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3193 
3194   Output Parameter:
3195 . cone - An array of points which are on the in-edges for point `p`, the length of `cone` is the result of `DMPlexGetConeSize()`
3196 
3197   Level: beginner
3198 
3199   Fortran Notes:
3200   `cone` must be declared with
3201 .vb
3202   PetscInt, pointer :: cone(:)
3203 .ve
3204 
3205   You must call `DMPlexRestoreCone()` after you finish using the array.
3206   `DMPlexRestoreCone()` is not needed/available in C.
3207 
3208 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
3209 @*/
3210 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
3211 {
3212   DM_Plex *mesh = (DM_Plex *)dm->data;
3213   PetscInt off;
3214 
3215   PetscFunctionBegin;
3216   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3217   PetscAssertPointer(cone, 3);
3218   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3219   *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3220   PetscFunctionReturn(PETSC_SUCCESS);
3221 }
3222 
3223 /*@
3224   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
3225 
3226   Not Collective
3227 
3228   Input Parameters:
3229 + dm - The `DMPLEX`
3230 - p  - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3231 
3232   Output Parameters:
3233 + pConesSection - `PetscSection` describing the layout of `pCones`
3234 - pCones        - An `IS` containing the points which are on the in-edges for the point set `p`
3235 
3236   Level: intermediate
3237 
3238 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
3239 @*/
3240 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PeOp PetscSection *pConesSection, PeOp IS *pCones)
3241 {
3242   PetscSection cs, newcs;
3243   PetscInt    *cones;
3244   PetscInt    *newarr = NULL;
3245   PetscInt     n;
3246 
3247   PetscFunctionBegin;
3248   PetscCall(DMPlexGetCones(dm, &cones));
3249   PetscCall(DMPlexGetConeSection(dm, &cs));
3250   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
3251   if (pConesSection) *pConesSection = newcs;
3252   if (pCones) {
3253     PetscCall(PetscSectionGetStorageSize(newcs, &n));
3254     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
3255   }
3256   PetscFunctionReturn(PETSC_SUCCESS);
3257 }
3258 
3259 /*@
3260   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
3261 
3262   Not Collective
3263 
3264   Input Parameters:
3265 + dm     - The `DMPLEX`
3266 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3267 
3268   Output Parameter:
3269 . expandedPoints - An `IS` containing the of vertices recursively expanded from input points
3270 
3271   Level: advanced
3272 
3273   Notes:
3274   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
3275 
3276   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
3277 
3278 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
3279           `DMPlexGetDepth()`, `IS`
3280 @*/
3281 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
3282 {
3283   IS      *expandedPointsAll;
3284   PetscInt depth;
3285 
3286   PetscFunctionBegin;
3287   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3288   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3289   PetscAssertPointer(expandedPoints, 3);
3290   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3291   *expandedPoints = expandedPointsAll[0];
3292   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
3293   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3294   PetscFunctionReturn(PETSC_SUCCESS);
3295 }
3296 
3297 /*@
3298   DMPlexGetConeRecursive - Expand each given point into its cone points and do that recursively until we end up just with vertices
3299   (DAG points of depth 0, i.e., without cones).
3300 
3301   Not Collective
3302 
3303   Input Parameters:
3304 + dm     - The `DMPLEX`
3305 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3306 
3307   Output Parameters:
3308 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3309 . expandedPoints - (optional) An array of index sets with recursively expanded cones
3310 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3311 
3312   Level: advanced
3313 
3314   Notes:
3315   Like `DMPlexGetConeTuple()` but recursive.
3316 
3317   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.
3318   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
3319 
3320   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\:
3321   (1) DAG points in `expandedPoints`[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
3322   (2) DAG points in `expandedPoints`[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
3323 
3324 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3325           `DMPlexGetDepth()`, `PetscSection`, `IS`
3326 @*/
3327 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PeOp PetscInt *depth, PeOp IS *expandedPoints[], PeOp PetscSection *sections[])
3328 {
3329   const PetscInt *arr0 = NULL, *cone = NULL;
3330   PetscInt       *arr = NULL, *newarr = NULL;
3331   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
3332   IS             *expandedPoints_;
3333   PetscSection   *sections_;
3334 
3335   PetscFunctionBegin;
3336   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3337   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3338   if (depth) PetscAssertPointer(depth, 3);
3339   if (expandedPoints) PetscAssertPointer(expandedPoints, 4);
3340   if (sections) PetscAssertPointer(sections, 5);
3341   PetscCall(ISGetLocalSize(points, &n));
3342   PetscCall(ISGetIndices(points, &arr0));
3343   PetscCall(DMPlexGetDepth(dm, &depth_));
3344   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
3345   PetscCall(PetscCalloc1(depth_, &sections_));
3346   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
3347   for (d = depth_ - 1; d >= 0; d--) {
3348     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
3349     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
3350     for (i = 0; i < n; i++) {
3351       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
3352       if (arr[i] >= start && arr[i] < end) {
3353         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
3354         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
3355       } else {
3356         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
3357       }
3358     }
3359     PetscCall(PetscSectionSetUp(sections_[d]));
3360     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
3361     PetscCall(PetscMalloc1(newn, &newarr));
3362     for (i = 0; i < n; i++) {
3363       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
3364       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
3365       if (cn > 1) {
3366         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
3367         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3368       } else {
3369         newarr[co] = arr[i];
3370       }
3371     }
3372     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3373     arr = newarr;
3374     n   = newn;
3375   }
3376   PetscCall(ISRestoreIndices(points, &arr0));
3377   *depth = depth_;
3378   if (expandedPoints) *expandedPoints = expandedPoints_;
3379   else {
3380     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3381     PetscCall(PetscFree(expandedPoints_));
3382   }
3383   if (sections) *sections = sections_;
3384   else {
3385     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3386     PetscCall(PetscFree(sections_));
3387   }
3388   PetscFunctionReturn(PETSC_SUCCESS);
3389 }
3390 
3391 /*@
3392   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3393 
3394   Not Collective
3395 
3396   Input Parameters:
3397 + dm     - The `DMPLEX`
3398 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3399 
3400   Output Parameters:
3401 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3402 . expandedPoints - (optional) An array of recursively expanded cones
3403 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3404 
3405   Level: advanced
3406 
3407   Note:
3408   See `DMPlexGetConeRecursive()`
3409 
3410 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3411           `DMPlexGetDepth()`, `IS`, `PetscSection`
3412 @*/
3413 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PeOp PetscInt *depth, PeOp IS *expandedPoints[], PeOp PetscSection *sections[])
3414 {
3415   PetscInt d, depth_;
3416 
3417   PetscFunctionBegin;
3418   PetscCall(DMPlexGetDepth(dm, &depth_));
3419   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3420   if (depth) *depth = 0;
3421   if (expandedPoints) {
3422     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&(*expandedPoints)[d]));
3423     PetscCall(PetscFree(*expandedPoints));
3424   }
3425   if (sections) {
3426     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&(*sections)[d]));
3427     PetscCall(PetscFree(*sections));
3428   }
3429   PetscFunctionReturn(PETSC_SUCCESS);
3430 }
3431 
3432 /*@
3433   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
3434 
3435   Not Collective
3436 
3437   Input Parameters:
3438 + dm   - The `DMPLEX`
3439 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3440 - cone - An array of points which are on the in-edges for point `p`, its length must have been previously provided with `DMPlexSetConeSize()`
3441 
3442   Level: beginner
3443 
3444   Note:
3445   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3446 
3447 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3448 @*/
3449 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3450 {
3451   DM_Plex *mesh = (DM_Plex *)dm->data;
3452   PetscInt dof, off, c;
3453 
3454   PetscFunctionBegin;
3455   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3456   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3457   if (dof) PetscAssertPointer(cone, 3);
3458   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3459   if (PetscDefined(USE_DEBUG)) {
3460     PetscInt pStart, pEnd;
3461     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3462     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);
3463     for (c = 0; c < dof; ++c) {
3464       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);
3465       mesh->cones[off + c] = cone[c];
3466     }
3467   } else {
3468     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3469   }
3470   PetscFunctionReturn(PETSC_SUCCESS);
3471 }
3472 
3473 /*@C
3474   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3475 
3476   Not Collective
3477 
3478   Input Parameters:
3479 + dm - The `DMPLEX`
3480 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3481 
3482   Output Parameter:
3483 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3484                     integer giving the prescription for cone traversal. Its length is given by the result of `DMPlexSetConeSize()`
3485 
3486   Level: beginner
3487 
3488   Note:
3489   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3490   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3491   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3492   with the identity.
3493 
3494   Fortran Notes:
3495   You must call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3496   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3497 
3498 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetConeSize()`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`,
3499           `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3500 @*/
3501 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3502 {
3503   DM_Plex *mesh = (DM_Plex *)dm->data;
3504   PetscInt off;
3505 
3506   PetscFunctionBegin;
3507   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3508   if (PetscDefined(USE_DEBUG)) {
3509     PetscInt dof;
3510     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3511     if (dof) PetscAssertPointer(coneOrientation, 3);
3512   }
3513   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3514 
3515   *coneOrientation = &mesh->coneOrientations[off];
3516   PetscFunctionReturn(PETSC_SUCCESS);
3517 }
3518 
3519 /*@
3520   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3521 
3522   Not Collective
3523 
3524   Input Parameters:
3525 + dm              - The `DMPLEX`
3526 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3527 - coneOrientation - An array of orientations. Its length is given by the result of `DMPlexSetConeSize()`
3528 
3529   Level: beginner
3530 
3531   Notes:
3532   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3533 
3534   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3535 
3536 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3537 @*/
3538 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3539 {
3540   DM_Plex *mesh = (DM_Plex *)dm->data;
3541   PetscInt pStart, pEnd;
3542   PetscInt dof, off, c;
3543 
3544   PetscFunctionBegin;
3545   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3546   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3547   if (dof) PetscAssertPointer(coneOrientation, 3);
3548   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3549   if (PetscDefined(USE_DEBUG)) {
3550     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3551     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);
3552     for (c = 0; c < dof; ++c) {
3553       PetscInt cdof, o = coneOrientation[c];
3554 
3555       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3556       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);
3557       mesh->coneOrientations[off + c] = o;
3558     }
3559   } else {
3560     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3561   }
3562   PetscFunctionReturn(PETSC_SUCCESS);
3563 }
3564 
3565 /*@
3566   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3567 
3568   Not Collective
3569 
3570   Input Parameters:
3571 + dm        - The `DMPLEX`
3572 . p         - The point, which must lie in the chart set with `DMPlexSetChart()`
3573 . conePos   - The local index in the cone where the point should be put
3574 - conePoint - The mesh point to insert
3575 
3576   Level: beginner
3577 
3578 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3579 @*/
3580 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3581 {
3582   DM_Plex *mesh = (DM_Plex *)dm->data;
3583   PetscInt pStart, pEnd;
3584   PetscInt dof, off;
3585 
3586   PetscFunctionBegin;
3587   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3588   if (PetscDefined(USE_DEBUG)) {
3589     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3590     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);
3591     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);
3592     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3593     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);
3594   }
3595   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3596   mesh->cones[off + conePos] = conePoint;
3597   PetscFunctionReturn(PETSC_SUCCESS);
3598 }
3599 
3600 /*@
3601   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3602 
3603   Not Collective
3604 
3605   Input Parameters:
3606 + dm              - The `DMPLEX`
3607 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3608 . conePos         - The local index in the cone where the point should be put
3609 - coneOrientation - The point orientation to insert
3610 
3611   Level: beginner
3612 
3613   Note:
3614   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3615 
3616 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3617 @*/
3618 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3619 {
3620   DM_Plex *mesh = (DM_Plex *)dm->data;
3621   PetscInt pStart, pEnd;
3622   PetscInt dof, off;
3623 
3624   PetscFunctionBegin;
3625   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3626   if (PetscDefined(USE_DEBUG)) {
3627     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3628     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);
3629     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3630     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);
3631   }
3632   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3633   mesh->coneOrientations[off + conePos] = coneOrientation;
3634   PetscFunctionReturn(PETSC_SUCCESS);
3635 }
3636 
3637 /*@C
3638   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3639 
3640   Not collective
3641 
3642   Input Parameters:
3643 + dm - The DMPlex
3644 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3645 
3646   Output Parameters:
3647 + cone - An array of points which are on the in-edges for point `p`
3648 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3649          integer giving the prescription for cone traversal.
3650 
3651   Level: beginner
3652 
3653   Notes:
3654   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3655   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3656   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3657   with the identity.
3658 
3659   You must also call `DMPlexRestoreOrientedCone()` after you finish using the returned array.
3660 
3661   Fortran Notes:
3662   `cone` and `ornt` must be declared with
3663 .vb
3664   PetscInt, pointer :: cone(:)
3665   PetscInt, pointer :: ornt(:)
3666 .ve
3667 
3668 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3669 @*/
3670 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, PeOp const PetscInt *cone[], PeOp const PetscInt *ornt[])
3671 {
3672   DM_Plex *mesh = (DM_Plex *)dm->data;
3673 
3674   PetscFunctionBegin;
3675   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3676   if (mesh->tr) {
3677     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3678   } else {
3679     PetscInt off;
3680     if (PetscDefined(USE_DEBUG)) {
3681       PetscInt dof;
3682       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3683       if (dof) {
3684         if (cone) PetscAssertPointer(cone, 3);
3685         if (ornt) PetscAssertPointer(ornt, 4);
3686       }
3687     }
3688     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3689     if (cone) *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3690     if (ornt) *ornt = PetscSafePointerPlusOffset(mesh->coneOrientations, off);
3691   }
3692   PetscFunctionReturn(PETSC_SUCCESS);
3693 }
3694 
3695 /*@C
3696   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG obtained with `DMPlexGetOrientedCone()`
3697 
3698   Not Collective
3699 
3700   Input Parameters:
3701 + dm   - The DMPlex
3702 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3703 . cone - An array of points which are on the in-edges for point p
3704 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3705          integer giving the prescription for cone traversal.
3706 
3707   Level: beginner
3708 
3709 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3710 @*/
3711 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3712 {
3713   DM_Plex *mesh = (DM_Plex *)dm->data;
3714 
3715   PetscFunctionBegin;
3716   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3717   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3718   PetscFunctionReturn(PETSC_SUCCESS);
3719 }
3720 
3721 /*@
3722   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3723 
3724   Not Collective
3725 
3726   Input Parameters:
3727 + dm - The `DMPLEX`
3728 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3729 
3730   Output Parameter:
3731 . size - The support size for point `p`
3732 
3733   Level: beginner
3734 
3735 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3736 @*/
3737 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3738 {
3739   DM_Plex *mesh = (DM_Plex *)dm->data;
3740 
3741   PetscFunctionBegin;
3742   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3743   PetscAssertPointer(size, 3);
3744   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3745   PetscFunctionReturn(PETSC_SUCCESS);
3746 }
3747 
3748 /*@
3749   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3750 
3751   Not Collective
3752 
3753   Input Parameters:
3754 + dm   - The `DMPLEX`
3755 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3756 - size - The support size for point `p`
3757 
3758   Level: beginner
3759 
3760   Note:
3761   This should be called after `DMPlexSetChart()`.
3762 
3763 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3764 @*/
3765 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3766 {
3767   DM_Plex *mesh = (DM_Plex *)dm->data;
3768 
3769   PetscFunctionBegin;
3770   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3771   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3772   PetscFunctionReturn(PETSC_SUCCESS);
3773 }
3774 
3775 /*@C
3776   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3777 
3778   Not Collective
3779 
3780   Input Parameters:
3781 + dm - The `DMPLEX`
3782 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3783 
3784   Output Parameter:
3785 . support - An array of points which are on the out-edges for point `p`, its length is that obtained from `DMPlexGetSupportSize()`
3786 
3787   Level: beginner
3788 
3789   Fortran Notes:
3790   `support` must be declared with
3791 .vb
3792   PetscInt, pointer :: support(:)
3793 .ve
3794 
3795   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3796   `DMPlexRestoreSupport()` is not needed/available in C.
3797 
3798 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3799 @*/
3800 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3801 {
3802   DM_Plex *mesh = (DM_Plex *)dm->data;
3803   PetscInt off;
3804 
3805   PetscFunctionBegin;
3806   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3807   PetscAssertPointer(support, 3);
3808   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3809   *support = PetscSafePointerPlusOffset(mesh->supports, off);
3810   PetscFunctionReturn(PETSC_SUCCESS);
3811 }
3812 
3813 /*@
3814   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3815 
3816   Not Collective
3817 
3818   Input Parameters:
3819 + dm      - The `DMPLEX`
3820 . p       - The point, which must lie in the chart set with `DMPlexSetChart()`
3821 - support - An array of points which are on the out-edges for point `p`, its length is that obtained from `DMPlexGetSupportSize()`
3822 
3823   Level: beginner
3824 
3825   Note:
3826   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3827 
3828 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3829 @*/
3830 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3831 {
3832   DM_Plex *mesh = (DM_Plex *)dm->data;
3833   PetscInt pStart, pEnd;
3834   PetscInt dof, off, c;
3835 
3836   PetscFunctionBegin;
3837   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3838   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3839   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3840   if (dof) PetscAssertPointer(support, 3);
3841   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3842   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);
3843   for (c = 0; c < dof; ++c) {
3844     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);
3845     mesh->supports[off + c] = support[c];
3846   }
3847   PetscFunctionReturn(PETSC_SUCCESS);
3848 }
3849 
3850 /*@
3851   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3852 
3853   Not Collective
3854 
3855   Input Parameters:
3856 + dm           - The `DMPLEX`
3857 . p            - The point, which must lie in the chart set with `DMPlexSetChart()`
3858 . supportPos   - The local index in the cone where the point should be put
3859 - supportPoint - The mesh point to insert
3860 
3861   Level: beginner
3862 
3863 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3864 @*/
3865 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3866 {
3867   DM_Plex *mesh = (DM_Plex *)dm->data;
3868   PetscInt pStart, pEnd;
3869   PetscInt dof, off;
3870 
3871   PetscFunctionBegin;
3872   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3873   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3874   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3875   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3876   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);
3877   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);
3878   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);
3879   mesh->supports[off + supportPos] = supportPoint;
3880   PetscFunctionReturn(PETSC_SUCCESS);
3881 }
3882 
3883 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3884 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3885 {
3886   switch (ct) {
3887   case DM_POLYTOPE_SEGMENT:
3888     if (o == -1) return -2;
3889     break;
3890   case DM_POLYTOPE_TRIANGLE:
3891     if (o == -3) return -1;
3892     if (o == -2) return -3;
3893     if (o == -1) return -2;
3894     break;
3895   case DM_POLYTOPE_QUADRILATERAL:
3896     if (o == -4) return -2;
3897     if (o == -3) return -1;
3898     if (o == -2) return -4;
3899     if (o == -1) return -3;
3900     break;
3901   default:
3902     return o;
3903   }
3904   return o;
3905 }
3906 
3907 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3908 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3909 {
3910   switch (ct) {
3911   case DM_POLYTOPE_SEGMENT:
3912     if ((o == -2) || (o == 1)) return -1;
3913     if (o == -1) return 0;
3914     break;
3915   case DM_POLYTOPE_TRIANGLE:
3916     if (o == -3) return -2;
3917     if (o == -2) return -1;
3918     if (o == -1) return -3;
3919     break;
3920   case DM_POLYTOPE_QUADRILATERAL:
3921     if (o == -4) return -2;
3922     if (o == -3) return -1;
3923     if (o == -2) return -4;
3924     if (o == -1) return -3;
3925     break;
3926   default:
3927     return o;
3928   }
3929   return o;
3930 }
3931 
3932 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3933 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3934 {
3935   PetscInt pStart, pEnd, p;
3936 
3937   PetscFunctionBegin;
3938   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3939   for (p = pStart; p < pEnd; ++p) {
3940     const PetscInt *cone, *ornt;
3941     PetscInt        coneSize, c;
3942 
3943     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3944     PetscCall(DMPlexGetCone(dm, p, &cone));
3945     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3946     for (c = 0; c < coneSize; ++c) {
3947       DMPolytopeType ct;
3948       const PetscInt o = ornt[c];
3949 
3950       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3951       switch (ct) {
3952       case DM_POLYTOPE_SEGMENT:
3953         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3954         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3955         break;
3956       case DM_POLYTOPE_TRIANGLE:
3957         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3958         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3959         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3960         break;
3961       case DM_POLYTOPE_QUADRILATERAL:
3962         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3963         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3964         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3965         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3966         break;
3967       default:
3968         break;
3969       }
3970     }
3971   }
3972   PetscFunctionReturn(PETSC_SUCCESS);
3973 }
3974 
3975 static inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3976 {
3977   DM_Plex *mesh = (DM_Plex *)dm->data;
3978 
3979   PetscFunctionBeginHot;
3980   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3981     if (useCone) {
3982       PetscCall(DMPlexGetConeSize(dm, p, size));
3983       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3984     } else {
3985       PetscCall(DMPlexGetSupportSize(dm, p, size));
3986       PetscCall(DMPlexGetSupport(dm, p, arr));
3987     }
3988   } else {
3989     if (useCone) {
3990       const PetscSection s   = mesh->coneSection;
3991       const PetscInt     ps  = p - s->pStart;
3992       const PetscInt     off = s->atlasOff[ps];
3993 
3994       *size = s->atlasDof[ps];
3995       *arr  = mesh->cones + off;
3996       *ornt = mesh->coneOrientations + off;
3997     } else {
3998       const PetscSection s   = mesh->supportSection;
3999       const PetscInt     ps  = p - s->pStart;
4000       const PetscInt     off = s->atlasOff[ps];
4001 
4002       *size = s->atlasDof[ps];
4003       *arr  = mesh->supports + off;
4004     }
4005   }
4006   PetscFunctionReturn(PETSC_SUCCESS);
4007 }
4008 
4009 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
4010 {
4011   DM_Plex *mesh = (DM_Plex *)dm->data;
4012 
4013   PetscFunctionBeginHot;
4014   if (PetscDefined(USE_DEBUG) || mesh->tr) {
4015     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
4016   }
4017   PetscFunctionReturn(PETSC_SUCCESS);
4018 }
4019 
4020 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4021 {
4022   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
4023   PetscInt       *closure;
4024   const PetscInt *tmp = NULL, *tmpO = NULL;
4025   PetscInt        off = 0, tmpSize, t;
4026 
4027   PetscFunctionBeginHot;
4028   if (ornt) {
4029     PetscCall(DMPlexGetCellType(dm, p, &ct));
4030     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;
4031   }
4032   if (*points) {
4033     closure = *points;
4034   } else {
4035     PetscInt maxConeSize, maxSupportSize;
4036     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
4037     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
4038   }
4039   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
4040   if (ct == DM_POLYTOPE_UNKNOWN) {
4041     closure[off++] = p;
4042     closure[off++] = 0;
4043     for (t = 0; t < tmpSize; ++t) {
4044       closure[off++] = tmp[t];
4045       closure[off++] = tmpO ? tmpO[t] : 0;
4046     }
4047   } else {
4048     const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, ornt);
4049 
4050     /* We assume that cells with a valid type have faces with a valid type */
4051     closure[off++] = p;
4052     closure[off++] = ornt;
4053     for (t = 0; t < tmpSize; ++t) {
4054       DMPolytopeType ft;
4055 
4056       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
4057       closure[off++] = tmp[arr[t]];
4058       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
4059     }
4060   }
4061   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
4062   if (numPoints) *numPoints = tmpSize + 1;
4063   if (points) *points = closure;
4064   PetscFunctionReturn(PETSC_SUCCESS);
4065 }
4066 
4067 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
4068 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
4069 {
4070   const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, o);
4071   const PetscInt *cone, *ornt;
4072   PetscInt       *pts, *closure = NULL;
4073   DMPolytopeType  ft;
4074   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
4075   PetscInt        dim, coneSize, c, d, clSize, cl;
4076 
4077   PetscFunctionBeginHot;
4078   PetscCall(DMGetDimension(dm, &dim));
4079   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
4080   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
4081   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
4082   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
4083   maxSize       = PetscMax(coneSeries, supportSeries);
4084   if (*points) {
4085     pts = *points;
4086   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
4087   c        = 0;
4088   pts[c++] = point;
4089   pts[c++] = o;
4090   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
4091   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
4092   for (cl = 0; cl < clSize * 2; cl += 2) {
4093     pts[c++] = closure[cl];
4094     pts[c++] = closure[cl + 1];
4095   }
4096   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
4097   for (cl = 0; cl < clSize * 2; cl += 2) {
4098     pts[c++] = closure[cl];
4099     pts[c++] = closure[cl + 1];
4100   }
4101   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
4102   for (d = 2; d < coneSize; ++d) {
4103     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
4104     pts[c++] = cone[arr[d * 2 + 0]];
4105     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
4106   }
4107   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
4108   if (dim >= 3) {
4109     for (d = 2; d < coneSize; ++d) {
4110       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
4111       const PetscInt *fcone, *fornt;
4112       PetscInt        fconeSize, fc, i;
4113 
4114       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
4115       const PetscInt *farr = DMPolytopeTypeGetArrangement(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
4116       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
4117       for (fc = 0; fc < fconeSize; ++fc) {
4118         const PetscInt cp = fcone[farr[fc * 2 + 0]];
4119         const PetscInt co = farr[fc * 2 + 1];
4120 
4121         for (i = 0; i < c; i += 2)
4122           if (pts[i] == cp) break;
4123         if (i == c) {
4124           PetscCall(DMPlexGetCellType(dm, cp, &ft));
4125           pts[c++] = cp;
4126           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
4127         }
4128       }
4129       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
4130     }
4131   }
4132   *numPoints = c / 2;
4133   *points    = pts;
4134   PetscFunctionReturn(PETSC_SUCCESS);
4135 }
4136 
4137 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4138 {
4139   DMPolytopeType ct;
4140   PetscInt      *closure, *fifo;
4141   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
4142   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
4143   PetscInt       depth, maxSize;
4144 
4145   PetscFunctionBeginHot;
4146   PetscCall(DMPlexGetDepth(dm, &depth));
4147   if (depth == 1) {
4148     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
4149     PetscFunctionReturn(PETSC_SUCCESS);
4150   }
4151   PetscCall(DMPlexGetCellType(dm, p, &ct));
4152   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;
4153   if (DMPolytopeTypeIsHybrid(ct) && ct != DM_POLYTOPE_POINT_PRISM_TENSOR) {
4154     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
4155     PetscFunctionReturn(PETSC_SUCCESS);
4156   }
4157   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
4158   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
4159   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
4160   maxSize       = PetscMax(coneSeries, supportSeries);
4161   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4162   if (*points) {
4163     closure = *points;
4164   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
4165   closure[closureSize++] = p;
4166   closure[closureSize++] = ornt;
4167   fifo[fifoSize++]       = p;
4168   fifo[fifoSize++]       = ornt;
4169   fifo[fifoSize++]       = ct;
4170   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
4171   while (fifoSize - fifoStart) {
4172     const PetscInt       q    = fifo[fifoStart++];
4173     const PetscInt       o    = fifo[fifoStart++];
4174     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
4175     const PetscInt      *qarr = DMPolytopeTypeGetArrangement(qt, o);
4176     const PetscInt      *tmp, *tmpO = NULL;
4177     PetscInt             tmpSize, t;
4178 
4179     if (PetscDefined(USE_DEBUG)) {
4180       PetscInt nO = DMPolytopeTypeGetNumArrangements(qt) / 2;
4181       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);
4182     }
4183     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4184     for (t = 0; t < tmpSize; ++t) {
4185       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
4186       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
4187       const PetscInt cp = tmp[ip];
4188       PetscCall(DMPlexGetCellType(dm, cp, &ct));
4189       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
4190       PetscInt       c;
4191 
4192       /* Check for duplicate */
4193       for (c = 0; c < closureSize; c += 2) {
4194         if (closure[c] == cp) break;
4195       }
4196       if (c == closureSize) {
4197         closure[closureSize++] = cp;
4198         closure[closureSize++] = co;
4199         fifo[fifoSize++]       = cp;
4200         fifo[fifoSize++]       = co;
4201         fifo[fifoSize++]       = ct;
4202       }
4203     }
4204     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4205   }
4206   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4207   if (numPoints) *numPoints = closureSize / 2;
4208   if (points) *points = closure;
4209   PetscFunctionReturn(PETSC_SUCCESS);
4210 }
4211 
4212 /*@C
4213   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
4214 
4215   Not Collective
4216 
4217   Input Parameters:
4218 + dm      - The `DMPLEX`
4219 . p       - The mesh point
4220 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
4221 
4222   Input/Output Parameter:
4223 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
4224            if *points is `NULL` on input, internal storage will be returned, use `DMPlexRestoreTransitiveClosure()`,
4225            otherwise the provided array is used to hold the values
4226 
4227   Output Parameter:
4228 . numPoints - The number of points in the closure, so `points` is of size 2*`numPoints`
4229 
4230   Level: beginner
4231 
4232   Note:
4233   If using internal storage (points is `NULL` on input), each call overwrites the last output.
4234 
4235   Fortran Notes:
4236   `points` must be declared with
4237 .vb
4238   PetscInt, pointer :: points(:)
4239 .ve
4240   and is always allocated by the function.
4241 
4242   Pass `PETSC_NULL_INTEGER` for `numPoints` if it is not needed
4243 
4244 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4245 @*/
4246 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4247 {
4248   PetscFunctionBeginHot;
4249   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4250   if (numPoints) PetscAssertPointer(numPoints, 4);
4251   if (points) PetscAssertPointer(points, 5);
4252   if (PetscDefined(USE_DEBUG)) {
4253     PetscInt pStart, pEnd;
4254     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4255     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);
4256   }
4257   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
4258   PetscFunctionReturn(PETSC_SUCCESS);
4259 }
4260 
4261 /*@C
4262   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
4263 
4264   Not Collective
4265 
4266   Input Parameters:
4267 + dm        - The `DMPLEX`
4268 . p         - The mesh point
4269 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
4270 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4271 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
4272 
4273   Level: beginner
4274 
4275   Note:
4276   If not using internal storage (points is not `NULL` on input), this call is unnecessary
4277 
4278 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4279 @*/
4280 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4281 {
4282   PetscFunctionBeginHot;
4283   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4284   if (numPoints) *numPoints = 0;
4285   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
4286   PetscFunctionReturn(PETSC_SUCCESS);
4287 }
4288 
4289 /*@
4290   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
4291 
4292   Not Collective
4293 
4294   Input Parameter:
4295 . dm - The `DMPLEX`
4296 
4297   Output Parameters:
4298 + maxConeSize    - The maximum number of in-edges
4299 - maxSupportSize - The maximum number of out-edges
4300 
4301   Level: beginner
4302 
4303 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
4304 @*/
4305 PetscErrorCode DMPlexGetMaxSizes(DM dm, PeOp PetscInt *maxConeSize, PeOp PetscInt *maxSupportSize)
4306 {
4307   DM_Plex *mesh = (DM_Plex *)dm->data;
4308 
4309   PetscFunctionBegin;
4310   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4311   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
4312   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
4313   PetscFunctionReturn(PETSC_SUCCESS);
4314 }
4315 
4316 PetscErrorCode DMSetUp_Plex(DM dm)
4317 {
4318   DM_Plex *mesh = (DM_Plex *)dm->data;
4319   PetscInt size, maxSupportSize;
4320 
4321   PetscFunctionBegin;
4322   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4323   PetscCall(PetscSectionSetUp(mesh->coneSection));
4324   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
4325   PetscCall(PetscMalloc1(size, &mesh->cones));
4326   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
4327   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4328   if (maxSupportSize) {
4329     PetscCall(PetscSectionSetUp(mesh->supportSection));
4330     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
4331     PetscCall(PetscMalloc1(size, &mesh->supports));
4332   }
4333   PetscFunctionReturn(PETSC_SUCCESS);
4334 }
4335 
4336 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
4337 {
4338   PetscFunctionBegin;
4339   if (subdm) PetscCall(DMClone(dm, subdm));
4340   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, NULL, NULL, is, subdm));
4341   if (subdm) (*subdm)->useNatural = dm->useNatural;
4342   if (dm->useNatural && dm->sfMigration) {
4343     PetscSF sfNatural;
4344 
4345     (*subdm)->sfMigration = dm->sfMigration;
4346     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
4347     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
4348     (*subdm)->sfNatural = sfNatural;
4349   }
4350   PetscFunctionReturn(PETSC_SUCCESS);
4351 }
4352 
4353 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
4354 {
4355   PetscInt i = 0;
4356 
4357   PetscFunctionBegin;
4358   PetscCall(DMClone(dms[0], superdm));
4359   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
4360   (*superdm)->useNatural = PETSC_FALSE;
4361   for (i = 0; i < len; i++) {
4362     if (dms[i]->useNatural && dms[i]->sfMigration) {
4363       PetscSF sfNatural;
4364 
4365       (*superdm)->sfMigration = dms[i]->sfMigration;
4366       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
4367       (*superdm)->useNatural = PETSC_TRUE;
4368       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
4369       (*superdm)->sfNatural = sfNatural;
4370       break;
4371     }
4372   }
4373   PetscFunctionReturn(PETSC_SUCCESS);
4374 }
4375 
4376 /*@
4377   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
4378 
4379   Not Collective
4380 
4381   Input Parameter:
4382 . dm - The `DMPLEX`
4383 
4384   Level: beginner
4385 
4386   Note:
4387   This should be called after all calls to `DMPlexSetCone()`
4388 
4389 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4390 @*/
4391 PetscErrorCode DMPlexSymmetrize(DM dm)
4392 {
4393   DM_Plex  *mesh = (DM_Plex *)dm->data;
4394   PetscInt *offsets;
4395   PetscInt  supportSize;
4396   PetscInt  pStart, pEnd, p;
4397 
4398   PetscFunctionBegin;
4399   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4400   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4401   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4402   /* Calculate support sizes */
4403   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4404   for (p = pStart; p < pEnd; ++p) {
4405     PetscInt dof, off, c;
4406 
4407     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4408     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4409     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4410   }
4411   PetscCall(PetscSectionSetUp(mesh->supportSection));
4412   /* Calculate supports */
4413   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4414   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4415   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4416   for (p = pStart; p < pEnd; ++p) {
4417     PetscInt dof, off, c;
4418 
4419     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4420     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4421     for (c = off; c < off + dof; ++c) {
4422       const PetscInt q = mesh->cones[c];
4423       PetscInt       offS;
4424 
4425       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4426 
4427       mesh->supports[offS + offsets[q]] = p;
4428       ++offsets[q];
4429     }
4430   }
4431   PetscCall(PetscFree(offsets));
4432   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4433   PetscFunctionReturn(PETSC_SUCCESS);
4434 }
4435 
4436 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4437 {
4438   IS stratumIS;
4439 
4440   PetscFunctionBegin;
4441   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4442   if (PetscDefined(USE_DEBUG)) {
4443     PetscInt  qStart, qEnd, numLevels, level;
4444     PetscBool overlap = PETSC_FALSE;
4445     PetscCall(DMLabelGetNumValues(label, &numLevels));
4446     for (level = 0; level < numLevels; level++) {
4447       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4448       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4449         overlap = PETSC_TRUE;
4450         break;
4451       }
4452     }
4453     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);
4454   }
4455   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4456   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4457   PetscCall(ISDestroy(&stratumIS));
4458   PetscFunctionReturn(PETSC_SUCCESS);
4459 }
4460 
4461 static PetscErrorCode DMPlexStratify_CellType_Private(DM dm, DMLabel label)
4462 {
4463   PetscInt *pMin, *pMax;
4464   PetscInt  pStart, pEnd;
4465   PetscInt  dmin = PETSC_INT_MAX, dmax = PETSC_INT_MIN;
4466 
4467   PetscFunctionBegin;
4468   {
4469     DMLabel label2;
4470 
4471     PetscCall(DMPlexGetCellTypeLabel(dm, &label2));
4472     PetscCall(PetscObjectViewFromOptions((PetscObject)label2, NULL, "-ct_view"));
4473   }
4474   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4475   for (PetscInt p = pStart; p < pEnd; ++p) {
4476     DMPolytopeType ct;
4477 
4478     PetscCall(DMPlexGetCellType(dm, p, &ct));
4479     dmin = PetscMin(DMPolytopeTypeGetDim(ct), dmin);
4480     dmax = PetscMax(DMPolytopeTypeGetDim(ct), dmax);
4481   }
4482   PetscCall(PetscMalloc2(dmax + 1, &pMin, dmax + 1, &pMax));
4483   for (PetscInt d = dmin; d <= dmax; ++d) {
4484     pMin[d] = PETSC_INT_MAX;
4485     pMax[d] = PETSC_INT_MIN;
4486   }
4487   for (PetscInt p = pStart; p < pEnd; ++p) {
4488     DMPolytopeType ct;
4489     PetscInt       d;
4490 
4491     PetscCall(DMPlexGetCellType(dm, p, &ct));
4492     d       = DMPolytopeTypeGetDim(ct);
4493     pMin[d] = PetscMin(p, pMin[d]);
4494     pMax[d] = PetscMax(p, pMax[d]);
4495   }
4496   for (PetscInt d = dmin; d <= dmax; ++d) {
4497     if (pMin[d] > pMax[d]) continue;
4498     PetscCall(DMPlexCreateDepthStratum(dm, label, d, pMin[d], pMax[d] + 1));
4499   }
4500   PetscCall(PetscFree2(pMin, pMax));
4501   PetscFunctionReturn(PETSC_SUCCESS);
4502 }
4503 
4504 static PetscErrorCode DMPlexStratify_Topological_Private(DM dm, DMLabel label)
4505 {
4506   PetscInt pStart, pEnd;
4507   PetscInt numRoots = 0, numLeaves = 0;
4508 
4509   PetscFunctionBegin;
4510   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4511   {
4512     /* Initialize roots and count leaves */
4513     PetscInt sMin = PETSC_INT_MAX;
4514     PetscInt sMax = PETSC_INT_MIN;
4515     PetscInt coneSize, supportSize;
4516 
4517     for (PetscInt p = pStart; p < pEnd; ++p) {
4518       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4519       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4520       if (!coneSize && supportSize) {
4521         sMin = PetscMin(p, sMin);
4522         sMax = PetscMax(p, sMax);
4523         ++numRoots;
4524       } else if (!supportSize && coneSize) {
4525         ++numLeaves;
4526       } else if (!supportSize && !coneSize) {
4527         /* Isolated points */
4528         sMin = PetscMin(p, sMin);
4529         sMax = PetscMax(p, sMax);
4530       }
4531     }
4532     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4533   }
4534 
4535   if (numRoots + numLeaves == (pEnd - pStart)) {
4536     PetscInt sMin = PETSC_INT_MAX;
4537     PetscInt sMax = PETSC_INT_MIN;
4538     PetscInt coneSize, supportSize;
4539 
4540     for (PetscInt p = pStart; p < pEnd; ++p) {
4541       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4542       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4543       if (!supportSize && coneSize) {
4544         sMin = PetscMin(p, sMin);
4545         sMax = PetscMax(p, sMax);
4546       }
4547     }
4548     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4549   } else {
4550     PetscInt level = 0;
4551     PetscInt qStart, qEnd;
4552 
4553     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4554     while (qEnd > qStart) {
4555       PetscInt sMin = PETSC_INT_MAX;
4556       PetscInt sMax = PETSC_INT_MIN;
4557 
4558       for (PetscInt q = qStart; q < qEnd; ++q) {
4559         const PetscInt *support;
4560         PetscInt        supportSize;
4561 
4562         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4563         PetscCall(DMPlexGetSupport(dm, q, &support));
4564         for (PetscInt s = 0; s < supportSize; ++s) {
4565           sMin = PetscMin(support[s], sMin);
4566           sMax = PetscMax(support[s], sMax);
4567         }
4568       }
4569       PetscCall(DMLabelGetNumValues(label, &level));
4570       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4571       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4572     }
4573   }
4574   PetscFunctionReturn(PETSC_SUCCESS);
4575 }
4576 
4577 /*@
4578   DMPlexStratify - Computes the strata for all points in the `DMPLEX`
4579 
4580   Collective
4581 
4582   Input Parameter:
4583 . dm - The `DMPLEX`
4584 
4585   Level: beginner
4586 
4587   Notes:
4588   The strata group all points of the same grade, and this function calculates the strata. This
4589   grade can be seen as the height (or depth) of the point in the DAG.
4590 
4591   The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4592   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram).
4593   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4594   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4595   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4596   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4597   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4598 
4599   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4600   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4601   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
4602   to interpolate only that one (e0), so that
4603 .vb
4604   cone(c0) = {e0, v2}
4605   cone(e0) = {v0, v1}
4606 .ve
4607   If `DMPlexStratify()` is run on this mesh, it will give depths
4608 .vb
4609    depth 0 = {v0, v1, v2}
4610    depth 1 = {e0, c0}
4611 .ve
4612   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4613 
4614   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4615 
4616 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4617 @*/
4618 PetscErrorCode DMPlexStratify(DM dm)
4619 {
4620   DM_Plex  *mesh = (DM_Plex *)dm->data;
4621   DMLabel   label;
4622   PetscBool flg = PETSC_FALSE;
4623 
4624   PetscFunctionBegin;
4625   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4626   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4627 
4628   // Create depth label
4629   PetscCall(DMRemoveLabel(dm, "depth", NULL));
4630   PetscCall(DMCreateLabel(dm, "depth"));
4631   PetscCall(DMPlexGetDepthLabel(dm, &label));
4632 
4633   PetscCall(PetscOptionsGetBool(NULL, dm->hdr.prefix, "-dm_plex_stratify_celltype", &flg, NULL));
4634   if (flg) PetscCall(DMPlexStratify_CellType_Private(dm, label));
4635   else PetscCall(DMPlexStratify_Topological_Private(dm, label));
4636 
4637   { /* just in case there is an empty process */
4638     PetscInt numValues, maxValues = 0, v;
4639 
4640     PetscCall(DMLabelGetNumValues(label, &numValues));
4641     PetscCallMPI(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4642     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4643   }
4644   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4645   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4646   PetscFunctionReturn(PETSC_SUCCESS);
4647 }
4648 
4649 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4650 {
4651   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4652   PetscInt       dim, depth, pheight, coneSize;
4653   PetscBool      preferTensor;
4654 
4655   PetscFunctionBeginHot;
4656   PetscCall(DMGetDimension(dm, &dim));
4657   PetscCall(DMPlexGetDepth(dm, &depth));
4658   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4659   PetscCall(DMPlexGetInterpolatePreferTensor(dm, &preferTensor));
4660   pheight = depth - pdepth;
4661   if (depth <= 1) {
4662     switch (pdepth) {
4663     case 0:
4664       ct = DM_POLYTOPE_POINT;
4665       break;
4666     case 1:
4667       switch (coneSize) {
4668       case 2:
4669         ct = DM_POLYTOPE_SEGMENT;
4670         break;
4671       case 3:
4672         ct = DM_POLYTOPE_TRIANGLE;
4673         break;
4674       case 4:
4675         switch (dim) {
4676         case 2:
4677           ct = DM_POLYTOPE_QUADRILATERAL;
4678           break;
4679         case 3:
4680           ct = DM_POLYTOPE_TETRAHEDRON;
4681           break;
4682         default:
4683           break;
4684         }
4685         break;
4686       case 5:
4687         ct = DM_POLYTOPE_PYRAMID;
4688         break;
4689       case 6:
4690         ct = preferTensor ? DM_POLYTOPE_TRI_PRISM_TENSOR : DM_POLYTOPE_TRI_PRISM;
4691         break;
4692       case 8:
4693         ct = DM_POLYTOPE_HEXAHEDRON;
4694         break;
4695       default:
4696         break;
4697       }
4698     }
4699   } else {
4700     if (pdepth == 0) {
4701       ct = DM_POLYTOPE_POINT;
4702     } else if (pheight == 0) {
4703       switch (dim) {
4704       case 1:
4705         switch (coneSize) {
4706         case 2:
4707           ct = DM_POLYTOPE_SEGMENT;
4708           break;
4709         default:
4710           break;
4711         }
4712         break;
4713       case 2:
4714         switch (coneSize) {
4715         case 3:
4716           ct = DM_POLYTOPE_TRIANGLE;
4717           break;
4718         case 4:
4719           ct = DM_POLYTOPE_QUADRILATERAL;
4720           break;
4721         default:
4722           break;
4723         }
4724         break;
4725       case 3:
4726         switch (coneSize) {
4727         case 4:
4728           ct = DM_POLYTOPE_TETRAHEDRON;
4729           break;
4730         case 5: {
4731           const PetscInt *cone;
4732           PetscInt        faceConeSize;
4733 
4734           PetscCall(DMPlexGetCone(dm, p, &cone));
4735           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4736           switch (faceConeSize) {
4737           case 3:
4738             ct = preferTensor ? DM_POLYTOPE_TRI_PRISM_TENSOR : DM_POLYTOPE_TRI_PRISM;
4739             break;
4740           case 4:
4741             ct = DM_POLYTOPE_PYRAMID;
4742             break;
4743           }
4744         } break;
4745         case 6:
4746           ct = DM_POLYTOPE_HEXAHEDRON;
4747           break;
4748         default:
4749           break;
4750         }
4751         break;
4752       default:
4753         break;
4754       }
4755     } else if (pheight > 0) {
4756       switch (coneSize) {
4757       case 2:
4758         ct = DM_POLYTOPE_SEGMENT;
4759         break;
4760       case 3:
4761         ct = DM_POLYTOPE_TRIANGLE;
4762         break;
4763       case 4:
4764         ct = DM_POLYTOPE_QUADRILATERAL;
4765         break;
4766       default:
4767         break;
4768       }
4769     }
4770   }
4771   *pt = ct;
4772   PetscFunctionReturn(PETSC_SUCCESS);
4773 }
4774 
4775 /*@
4776   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4777 
4778   Collective
4779 
4780   Input Parameter:
4781 . dm - The `DMPLEX`
4782 
4783   Level: developer
4784 
4785   Note:
4786   This function is normally called automatically when a cell type is requested. It creates an
4787   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4788   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4789 
4790   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4791 
4792 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4793 @*/
4794 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4795 {
4796   DM_Plex *mesh;
4797   DMLabel  ctLabel;
4798   PetscInt pStart, pEnd, p;
4799 
4800   PetscFunctionBegin;
4801   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4802   mesh = (DM_Plex *)dm->data;
4803   PetscCall(DMCreateLabel(dm, "celltype"));
4804   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4805   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4806   PetscCall(PetscFree(mesh->cellTypes));
4807   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4808   for (p = pStart; p < pEnd; ++p) {
4809     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4810     PetscInt       pdepth;
4811 
4812     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4813     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4814     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]);
4815     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4816     mesh->cellTypes[p - pStart].value_as_uint8 = (uint8_t)ct;
4817   }
4818   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4819   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4820   PetscFunctionReturn(PETSC_SUCCESS);
4821 }
4822 
4823 /*@C
4824   DMPlexGetJoin - Get an array for the join of the set of points
4825 
4826   Not Collective
4827 
4828   Input Parameters:
4829 + dm        - The `DMPLEX` object
4830 . numPoints - The number of input points for the join
4831 - points    - The input points
4832 
4833   Output Parameters:
4834 + numCoveredPoints - The number of points in the join
4835 - coveredPoints    - The points in the join
4836 
4837   Level: intermediate
4838 
4839   Note:
4840   Currently, this is restricted to a single level join
4841 
4842   Fortran Notes:
4843   `converedPoints` must be declared with
4844 .vb
4845   PetscInt, pointer :: coveredPints(:)
4846 .ve
4847 
4848 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4849 @*/
4850 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4851 {
4852   DM_Plex  *mesh = (DM_Plex *)dm->data;
4853   PetscInt *join[2];
4854   PetscInt  joinSize, i = 0;
4855   PetscInt  dof, off, p, c, m;
4856   PetscInt  maxSupportSize;
4857 
4858   PetscFunctionBegin;
4859   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4860   PetscAssertPointer(points, 3);
4861   PetscAssertPointer(numCoveredPoints, 4);
4862   PetscAssertPointer(coveredPoints, 5);
4863   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4864   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4865   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4866   /* Copy in support of first point */
4867   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4868   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4869   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4870   /* Check each successive support */
4871   for (p = 1; p < numPoints; ++p) {
4872     PetscInt newJoinSize = 0;
4873 
4874     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4875     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4876     for (c = 0; c < dof; ++c) {
4877       const PetscInt point = mesh->supports[off + c];
4878 
4879       for (m = 0; m < joinSize; ++m) {
4880         if (point == join[i][m]) {
4881           join[1 - i][newJoinSize++] = point;
4882           break;
4883         }
4884       }
4885     }
4886     joinSize = newJoinSize;
4887     i        = 1 - i;
4888   }
4889   *numCoveredPoints = joinSize;
4890   *coveredPoints    = join[i];
4891   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4892   PetscFunctionReturn(PETSC_SUCCESS);
4893 }
4894 
4895 /*@C
4896   DMPlexRestoreJoin - Restore an array for the join of the set of points obtained with `DMPlexGetJoin()`
4897 
4898   Not Collective
4899 
4900   Input Parameters:
4901 + dm        - The `DMPLEX` object
4902 . numPoints - The number of input points for the join
4903 - points    - The input points
4904 
4905   Output Parameters:
4906 + numCoveredPoints - The number of points in the join
4907 - coveredPoints    - The points in the join
4908 
4909   Level: intermediate
4910 
4911   Fortran Notes:
4912   `converedPoints` must be declared with
4913 .vb
4914   PetscInt, pointer :: coveredPoints(:)
4915 .ve
4916 
4917   Pass `PETSC_NULL_INTEGER` for `numCoveredPoints` if it is not needed
4918 
4919 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4920 @*/
4921 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4922 {
4923   PetscFunctionBegin;
4924   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4925   if (points) PetscAssertPointer(points, 3);
4926   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4927   PetscAssertPointer(coveredPoints, 5);
4928   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4929   if (numCoveredPoints) *numCoveredPoints = 0;
4930   PetscFunctionReturn(PETSC_SUCCESS);
4931 }
4932 
4933 /*@C
4934   DMPlexGetFullJoin - Get an array for the join of the set of points
4935 
4936   Not Collective
4937 
4938   Input Parameters:
4939 + dm        - The `DMPLEX` object
4940 . numPoints - The number of input points for the join
4941 - points    - The input points, its length is `numPoints`
4942 
4943   Output Parameters:
4944 + numCoveredPoints - The number of points in the join
4945 - coveredPoints    - The points in the join, its length is `numCoveredPoints`
4946 
4947   Level: intermediate
4948 
4949   Fortran Notes:
4950 .vb
4951   PetscInt, pointer :: coveredPints(:)
4952 .ve
4953 
4954 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4955 @*/
4956 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4957 {
4958   PetscInt *offsets, **closures;
4959   PetscInt *join[2];
4960   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4961   PetscInt  p, d, c, m, ms;
4962 
4963   PetscFunctionBegin;
4964   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4965   PetscAssertPointer(points, 3);
4966   PetscAssertPointer(numCoveredPoints, 4);
4967   PetscAssertPointer(coveredPoints, 5);
4968 
4969   PetscCall(DMPlexGetDepth(dm, &depth));
4970   PetscCall(PetscCalloc1(numPoints, &closures));
4971   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4972   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4973   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4974   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4975   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4976 
4977   for (p = 0; p < numPoints; ++p) {
4978     PetscInt closureSize;
4979 
4980     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4981 
4982     offsets[p * (depth + 2) + 0] = 0;
4983     for (d = 0; d < depth + 1; ++d) {
4984       PetscInt pStart, pEnd, i;
4985 
4986       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4987       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4988         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4989           offsets[p * (depth + 2) + d + 1] = i;
4990           break;
4991         }
4992       }
4993       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4994     }
4995     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);
4996   }
4997   for (d = 0; d < depth + 1; ++d) {
4998     PetscInt dof;
4999 
5000     /* Copy in support of first point */
5001     dof = offsets[d + 1] - offsets[d];
5002     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
5003     /* Check each successive cone */
5004     for (p = 1; p < numPoints && joinSize; ++p) {
5005       PetscInt newJoinSize = 0;
5006 
5007       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
5008       for (c = 0; c < dof; ++c) {
5009         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
5010 
5011         for (m = 0; m < joinSize; ++m) {
5012           if (point == join[i][m]) {
5013             join[1 - i][newJoinSize++] = point;
5014             break;
5015           }
5016         }
5017       }
5018       joinSize = newJoinSize;
5019       i        = 1 - i;
5020     }
5021     if (joinSize) break;
5022   }
5023   *numCoveredPoints = joinSize;
5024   *coveredPoints    = join[i];
5025   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
5026   PetscCall(PetscFree(closures));
5027   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
5028   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
5029   PetscFunctionReturn(PETSC_SUCCESS);
5030 }
5031 
5032 /*@C
5033   DMPlexGetMeet - Get an array for the meet of the set of points
5034 
5035   Not Collective
5036 
5037   Input Parameters:
5038 + dm        - The `DMPLEX` object
5039 . numPoints - The number of input points for the meet
5040 - points    - The input points, of length `numPoints`
5041 
5042   Output Parameters:
5043 + numCoveringPoints - The number of points in the meet
5044 - coveringPoints    - The points in the meet, of length `numCoveringPoints`
5045 
5046   Level: intermediate
5047 
5048   Note:
5049   Currently, this is restricted to a single level meet
5050 
5051   Fortran Note:
5052   `coveringPoints` must be declared with
5053 .vb
5054   PetscInt, pointer :: coveringPoints(:)
5055 .ve
5056 
5057 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
5058 @*/
5059 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt *coveringPoints[])
5060 {
5061   DM_Plex  *mesh = (DM_Plex *)dm->data;
5062   PetscInt *meet[2];
5063   PetscInt  meetSize, i = 0;
5064   PetscInt  dof, off, p, c, m;
5065   PetscInt  maxConeSize;
5066 
5067   PetscFunctionBegin;
5068   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5069   PetscAssertPointer(points, 3);
5070   PetscAssertPointer(numCoveringPoints, 4);
5071   PetscAssertPointer(coveringPoints, 5);
5072   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
5073   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
5074   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
5075   /* Copy in cone of first point */
5076   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
5077   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
5078   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
5079   /* Check each successive cone */
5080   for (p = 1; p < numPoints; ++p) {
5081     PetscInt newMeetSize = 0;
5082 
5083     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
5084     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
5085     for (c = 0; c < dof; ++c) {
5086       const PetscInt point = mesh->cones[off + c];
5087 
5088       for (m = 0; m < meetSize; ++m) {
5089         if (point == meet[i][m]) {
5090           meet[1 - i][newMeetSize++] = point;
5091           break;
5092         }
5093       }
5094     }
5095     meetSize = newMeetSize;
5096     i        = 1 - i;
5097   }
5098   *numCoveringPoints = meetSize;
5099   *coveringPoints    = meet[i];
5100   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
5101   PetscFunctionReturn(PETSC_SUCCESS);
5102 }
5103 
5104 /*@C
5105   DMPlexRestoreMeet - Restore an array for the meet of the set of points obtained with `DMPlexGetMeet()`
5106 
5107   Not Collective
5108 
5109   Input Parameters:
5110 + dm        - The `DMPLEX` object
5111 . numPoints - The number of input points for the meet
5112 - points    - The input points
5113 
5114   Output Parameters:
5115 + numCoveredPoints - The number of points in the meet
5116 - coveredPoints    - The points in the meet
5117 
5118   Level: intermediate
5119 
5120 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
5121 @*/
5122 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
5123 {
5124   PetscFunctionBegin;
5125   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5126   if (points) PetscAssertPointer(points, 3);
5127   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
5128   PetscAssertPointer(coveredPoints, 5);
5129   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
5130   if (numCoveredPoints) *numCoveredPoints = 0;
5131   PetscFunctionReturn(PETSC_SUCCESS);
5132 }
5133 
5134 /*@C
5135   DMPlexGetFullMeet - Get an array for the meet of the set of points
5136 
5137   Not Collective
5138 
5139   Input Parameters:
5140 + dm        - The `DMPLEX` object
5141 . numPoints - The number of input points for the meet
5142 - points    - The input points, of length  `numPoints`
5143 
5144   Output Parameters:
5145 + numCoveredPoints - The number of points in the meet
5146 - coveredPoints    - The points in the meet, of length  `numCoveredPoints`
5147 
5148   Level: intermediate
5149 
5150   Fortran Notes:
5151   `coveredPoints` must be declared with
5152 .vb
5153   PetscInt, pointer :: coveredPoints(:)
5154 .ve
5155 
5156 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
5157 @*/
5158 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
5159 {
5160   PetscInt *offsets, **closures;
5161   PetscInt *meet[2];
5162   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
5163   PetscInt  p, h, c, m, mc;
5164 
5165   PetscFunctionBegin;
5166   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5167   PetscAssertPointer(points, 3);
5168   PetscAssertPointer(numCoveredPoints, 4);
5169   PetscAssertPointer(coveredPoints, 5);
5170 
5171   PetscCall(DMPlexGetDepth(dm, &height));
5172   PetscCall(PetscMalloc1(numPoints, &closures));
5173   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5174   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
5175   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
5176   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
5177   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
5178 
5179   for (p = 0; p < numPoints; ++p) {
5180     PetscInt closureSize;
5181 
5182     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
5183 
5184     offsets[p * (height + 2) + 0] = 0;
5185     for (h = 0; h < height + 1; ++h) {
5186       PetscInt pStart, pEnd, i;
5187 
5188       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
5189       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
5190         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
5191           offsets[p * (height + 2) + h + 1] = i;
5192           break;
5193         }
5194       }
5195       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
5196     }
5197     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);
5198   }
5199   for (h = 0; h < height + 1; ++h) {
5200     PetscInt dof;
5201 
5202     /* Copy in cone of first point */
5203     dof = offsets[h + 1] - offsets[h];
5204     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
5205     /* Check each successive cone */
5206     for (p = 1; p < numPoints && meetSize; ++p) {
5207       PetscInt newMeetSize = 0;
5208 
5209       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
5210       for (c = 0; c < dof; ++c) {
5211         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
5212 
5213         for (m = 0; m < meetSize; ++m) {
5214           if (point == meet[i][m]) {
5215             meet[1 - i][newMeetSize++] = point;
5216             break;
5217           }
5218         }
5219       }
5220       meetSize = newMeetSize;
5221       i        = 1 - i;
5222     }
5223     if (meetSize) break;
5224   }
5225   *numCoveredPoints = meetSize;
5226   *coveredPoints    = meet[i];
5227   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
5228   PetscCall(PetscFree(closures));
5229   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5230   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
5231   PetscFunctionReturn(PETSC_SUCCESS);
5232 }
5233 
5234 /*@
5235   DMPlexEqual - Determine if two `DM` have the same topology
5236 
5237   Not Collective
5238 
5239   Input Parameters:
5240 + dmA - A `DMPLEX` object
5241 - dmB - A `DMPLEX` object
5242 
5243   Output Parameter:
5244 . equal - `PETSC_TRUE` if the topologies are identical
5245 
5246   Level: intermediate
5247 
5248   Note:
5249   We are not solving graph isomorphism, so we do not permute.
5250 
5251 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5252 @*/
5253 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
5254 {
5255   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
5256 
5257   PetscFunctionBegin;
5258   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
5259   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
5260   PetscAssertPointer(equal, 3);
5261 
5262   *equal = PETSC_FALSE;
5263   PetscCall(DMPlexGetDepth(dmA, &depth));
5264   PetscCall(DMPlexGetDepth(dmB, &depthB));
5265   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
5266   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
5267   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
5268   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
5269   for (p = pStart; p < pEnd; ++p) {
5270     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
5271     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
5272 
5273     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
5274     PetscCall(DMPlexGetCone(dmA, p, &cone));
5275     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
5276     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
5277     PetscCall(DMPlexGetCone(dmB, p, &coneB));
5278     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
5279     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5280     for (c = 0; c < coneSize; ++c) {
5281       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5282       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5283     }
5284     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
5285     PetscCall(DMPlexGetSupport(dmA, p, &support));
5286     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
5287     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
5288     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5289     for (s = 0; s < supportSize; ++s) {
5290       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
5291     }
5292   }
5293   *equal = PETSC_TRUE;
5294   PetscFunctionReturn(PETSC_SUCCESS);
5295 }
5296 
5297 /*@
5298   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
5299 
5300   Not Collective
5301 
5302   Input Parameters:
5303 + dm         - The `DMPLEX`
5304 . cellDim    - The cell dimension
5305 - numCorners - The number of vertices on a cell
5306 
5307   Output Parameter:
5308 . numFaceVertices - The number of vertices on a face
5309 
5310   Level: developer
5311 
5312   Note:
5313   Of course this can only work for a restricted set of symmetric shapes
5314 
5315 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5316 @*/
5317 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
5318 {
5319   MPI_Comm comm;
5320 
5321   PetscFunctionBegin;
5322   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
5323   PetscAssertPointer(numFaceVertices, 4);
5324   switch (cellDim) {
5325   case 0:
5326     *numFaceVertices = 0;
5327     break;
5328   case 1:
5329     *numFaceVertices = 1;
5330     break;
5331   case 2:
5332     switch (numCorners) {
5333     case 3:                 /* triangle */
5334       *numFaceVertices = 2; /* Edge has 2 vertices */
5335       break;
5336     case 4:                 /* quadrilateral */
5337       *numFaceVertices = 2; /* Edge has 2 vertices */
5338       break;
5339     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
5340       *numFaceVertices = 3; /* Edge has 3 vertices */
5341       break;
5342     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
5343       *numFaceVertices = 3; /* Edge has 3 vertices */
5344       break;
5345     default:
5346       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5347     }
5348     break;
5349   case 3:
5350     switch (numCorners) {
5351     case 4:                 /* tetradehdron */
5352       *numFaceVertices = 3; /* Face has 3 vertices */
5353       break;
5354     case 6:                 /* tet cohesive cells */
5355       *numFaceVertices = 4; /* Face has 4 vertices */
5356       break;
5357     case 8:                 /* hexahedron */
5358       *numFaceVertices = 4; /* Face has 4 vertices */
5359       break;
5360     case 9:                 /* tet cohesive Lagrange cells */
5361       *numFaceVertices = 6; /* Face has 6 vertices */
5362       break;
5363     case 10:                /* quadratic tetrahedron */
5364       *numFaceVertices = 6; /* Face has 6 vertices */
5365       break;
5366     case 12:                /* hex cohesive Lagrange cells */
5367       *numFaceVertices = 6; /* Face has 6 vertices */
5368       break;
5369     case 18:                /* quadratic tet cohesive Lagrange cells */
5370       *numFaceVertices = 6; /* Face has 6 vertices */
5371       break;
5372     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
5373       *numFaceVertices = 9; /* Face has 9 vertices */
5374       break;
5375     default:
5376       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5377     }
5378     break;
5379   default:
5380     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
5381   }
5382   PetscFunctionReturn(PETSC_SUCCESS);
5383 }
5384 
5385 /*@
5386   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
5387 
5388   Not Collective
5389 
5390   Input Parameter:
5391 . dm - The `DMPLEX` object
5392 
5393   Output Parameter:
5394 . depthLabel - The `DMLabel` recording point depth
5395 
5396   Level: developer
5397 
5398 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
5399 @*/
5400 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
5401 {
5402   PetscFunctionBegin;
5403   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5404   PetscAssertPointer(depthLabel, 2);
5405   *depthLabel = dm->depthLabel;
5406   PetscFunctionReturn(PETSC_SUCCESS);
5407 }
5408 
5409 /*@
5410   DMPlexGetDepth - Get the depth of the DAG representing this mesh
5411 
5412   Not Collective
5413 
5414   Input Parameter:
5415 . dm - The `DMPLEX` object
5416 
5417   Output Parameter:
5418 . depth - The number of strata (breadth first levels) in the DAG
5419 
5420   Level: developer
5421 
5422   Notes:
5423   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
5424 
5425   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
5426 
5427   An empty mesh gives -1.
5428 
5429 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
5430 @*/
5431 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
5432 {
5433   DM_Plex *mesh = (DM_Plex *)dm->data;
5434   DMLabel  label;
5435   PetscInt d = -1;
5436 
5437   PetscFunctionBegin;
5438   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5439   PetscAssertPointer(depth, 2);
5440   if (mesh->tr) {
5441     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
5442   } else {
5443     PetscCall(DMPlexGetDepthLabel(dm, &label));
5444     // Allow missing depths
5445     if (label) PetscCall(DMLabelGetValueBounds(label, NULL, &d));
5446     *depth = d;
5447   }
5448   PetscFunctionReturn(PETSC_SUCCESS);
5449 }
5450 
5451 /*@
5452   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
5453 
5454   Not Collective
5455 
5456   Input Parameters:
5457 + dm    - The `DMPLEX` object
5458 - depth - The requested depth
5459 
5460   Output Parameters:
5461 + start - The first point at this `depth`
5462 - end   - One beyond the last point at this `depth`
5463 
5464   Level: developer
5465 
5466   Notes:
5467   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5468   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5469   higher dimension, e.g., "edges".
5470 
5471 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5472 @*/
5473 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PeOp PetscInt *start, PeOp PetscInt *end)
5474 {
5475   DM_Plex *mesh = (DM_Plex *)dm->data;
5476   DMLabel  label;
5477   PetscInt pStart, pEnd;
5478 
5479   PetscFunctionBegin;
5480   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5481   if (start) {
5482     PetscAssertPointer(start, 3);
5483     *start = 0;
5484   }
5485   if (end) {
5486     PetscAssertPointer(end, 4);
5487     *end = 0;
5488   }
5489   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5490   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5491   if (depth < 0) {
5492     if (start) *start = pStart;
5493     if (end) *end = pEnd;
5494     PetscFunctionReturn(PETSC_SUCCESS);
5495   }
5496   if (mesh->tr) {
5497     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5498   } else {
5499     PetscCall(DMPlexGetDepthLabel(dm, &label));
5500     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5501     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5502   }
5503   PetscFunctionReturn(PETSC_SUCCESS);
5504 }
5505 
5506 /*@
5507   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5508 
5509   Not Collective
5510 
5511   Input Parameters:
5512 + dm     - The `DMPLEX` object
5513 - height - The requested height
5514 
5515   Output Parameters:
5516 + start - The first point at this `height`
5517 - end   - One beyond the last point at this `height`
5518 
5519   Level: developer
5520 
5521   Notes:
5522   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5523   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5524   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5525 
5526 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5527 @*/
5528 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PeOp PetscInt *start, PeOp PetscInt *end)
5529 {
5530   DMLabel  label;
5531   PetscInt depth, pStart, pEnd;
5532 
5533   PetscFunctionBegin;
5534   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5535   if (start) {
5536     PetscAssertPointer(start, 3);
5537     *start = 0;
5538   }
5539   if (end) {
5540     PetscAssertPointer(end, 4);
5541     *end = 0;
5542   }
5543   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5544   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5545   if (height < 0) {
5546     if (start) *start = pStart;
5547     if (end) *end = pEnd;
5548     PetscFunctionReturn(PETSC_SUCCESS);
5549   }
5550   PetscCall(DMPlexGetDepthLabel(dm, &label));
5551   if (label) PetscCall(DMLabelGetNumValues(label, &depth));
5552   else PetscCall(DMGetDimension(dm, &depth));
5553   PetscCheck(depth >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Depth not yet computed");
5554   PetscCall(DMPlexGetDepthStratum(dm, depth - 1 - height, start, end));
5555   PetscFunctionReturn(PETSC_SUCCESS);
5556 }
5557 
5558 /*@
5559   DMPlexGetPointDepth - Get the `depth` of a given point
5560 
5561   Not Collective
5562 
5563   Input Parameters:
5564 + dm    - The `DMPLEX` object
5565 - point - The point
5566 
5567   Output Parameter:
5568 . depth - The depth of the `point`
5569 
5570   Level: intermediate
5571 
5572 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5573 @*/
5574 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5575 {
5576   PetscFunctionBegin;
5577   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5578   PetscAssertPointer(depth, 3);
5579   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5580   PetscFunctionReturn(PETSC_SUCCESS);
5581 }
5582 
5583 /*@
5584   DMPlexGetPointHeight - Get the `height` of a given point
5585 
5586   Not Collective
5587 
5588   Input Parameters:
5589 + dm    - The `DMPLEX` object
5590 - point - The point
5591 
5592   Output Parameter:
5593 . height - The height of the `point`
5594 
5595   Level: intermediate
5596 
5597 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5598 @*/
5599 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5600 {
5601   PetscInt n, pDepth;
5602 
5603   PetscFunctionBegin;
5604   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5605   PetscAssertPointer(height, 3);
5606   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5607   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5608   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5609   PetscFunctionReturn(PETSC_SUCCESS);
5610 }
5611 
5612 /*@
5613   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5614 
5615   Not Collective
5616 
5617   Input Parameter:
5618 . dm - The `DMPLEX` object
5619 
5620   Output Parameter:
5621 . celltypeLabel - The `DMLabel` recording cell polytope type
5622 
5623   Level: developer
5624 
5625   Note:
5626   This function will trigger automatica computation of cell types. This can be disabled by calling
5627   `DMCreateLabel`(dm, "celltype") beforehand.
5628 
5629 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5630 @*/
5631 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5632 {
5633   PetscFunctionBegin;
5634   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5635   PetscAssertPointer(celltypeLabel, 2);
5636   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5637   *celltypeLabel = dm->celltypeLabel;
5638   PetscFunctionReturn(PETSC_SUCCESS);
5639 }
5640 
5641 /*@
5642   DMPlexGetCellType - Get the polytope type of a given cell
5643 
5644   Not Collective
5645 
5646   Input Parameters:
5647 + dm   - The `DMPLEX` object
5648 - cell - The cell
5649 
5650   Output Parameter:
5651 . celltype - The polytope type of the cell
5652 
5653   Level: intermediate
5654 
5655 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5656 @*/
5657 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5658 {
5659   DM_Plex *mesh = (DM_Plex *)dm->data;
5660   DMLabel  label;
5661   PetscInt ct;
5662 
5663   PetscFunctionBegin;
5664   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5665   PetscAssertPointer(celltype, 3);
5666   if (mesh->tr) {
5667     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5668   } else {
5669     PetscInt pStart, pEnd;
5670 
5671     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5672     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5673       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5674       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5675       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5676       for (PetscInt p = pStart; p < pEnd; p++) {
5677         PetscCall(DMLabelGetValue(label, p, &ct));
5678         mesh->cellTypes[p - pStart].value_as_uint8 = (uint8_t)ct;
5679       }
5680     }
5681     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5682     if (PetscDefined(USE_DEBUG)) {
5683       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5684       PetscCall(DMLabelGetValue(label, cell, &ct));
5685       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5686       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5687     }
5688   }
5689   PetscFunctionReturn(PETSC_SUCCESS);
5690 }
5691 
5692 /*@
5693   DMPlexSetCellType - Set the polytope type of a given cell
5694 
5695   Not Collective
5696 
5697   Input Parameters:
5698 + dm       - The `DMPLEX` object
5699 . cell     - The cell
5700 - celltype - The polytope type of the cell
5701 
5702   Level: advanced
5703 
5704   Note:
5705   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5706   is executed. This function will override the computed type. However, if automatic classification will not succeed
5707   and a user wants to manually specify all types, the classification must be disabled by calling
5708   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5709 
5710 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5711 @*/
5712 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5713 {
5714   DM_Plex *mesh = (DM_Plex *)dm->data;
5715   DMLabel  label;
5716   PetscInt pStart, pEnd;
5717 
5718   PetscFunctionBegin;
5719   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5720   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5721   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5722   PetscCall(DMLabelSetValue(label, cell, celltype));
5723   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5724   mesh->cellTypes[cell - pStart].value_as_uint8 = (uint8_t)celltype;
5725   PetscFunctionReturn(PETSC_SUCCESS);
5726 }
5727 
5728 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5729 {
5730   PetscSection section;
5731   PetscInt     maxHeight;
5732   const char  *prefix;
5733 
5734   PetscFunctionBegin;
5735   PetscCall(DMClone(dm, cdm));
5736   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5737   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5738   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5739   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5740   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5741   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5742   PetscCall(DMSetLocalSection(*cdm, section));
5743   PetscCall(PetscSectionDestroy(&section));
5744 
5745   PetscCall(DMSetNumFields(*cdm, 1));
5746   PetscCall(DMCreateDS(*cdm));
5747   (*cdm)->cloneOpts = PETSC_TRUE;
5748   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5749   PetscFunctionReturn(PETSC_SUCCESS);
5750 }
5751 
5752 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5753 {
5754   Vec coordsLocal, cellCoordsLocal;
5755   DM  coordsDM, cellCoordsDM;
5756 
5757   PetscFunctionBegin;
5758   *field = NULL;
5759   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5760   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5761   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5762   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5763   if (coordsLocal && coordsDM) {
5764     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5765     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5766   }
5767   PetscFunctionReturn(PETSC_SUCCESS);
5768 }
5769 
5770 /*@
5771   DMPlexGetConeSection - Return a section which describes the layout of cone data
5772 
5773   Not Collective
5774 
5775   Input Parameter:
5776 . dm - The `DMPLEX` object
5777 
5778   Output Parameter:
5779 . section - The `PetscSection` object
5780 
5781   Level: developer
5782 
5783 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5784 @*/
5785 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5786 {
5787   DM_Plex *mesh = (DM_Plex *)dm->data;
5788 
5789   PetscFunctionBegin;
5790   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5791   if (section) *section = mesh->coneSection;
5792   PetscFunctionReturn(PETSC_SUCCESS);
5793 }
5794 
5795 /*@
5796   DMPlexGetSupportSection - Return a section which describes the layout of support data
5797 
5798   Not Collective
5799 
5800   Input Parameter:
5801 . dm - The `DMPLEX` object
5802 
5803   Output Parameter:
5804 . section - The `PetscSection` object
5805 
5806   Level: developer
5807 
5808 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5809 @*/
5810 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5811 {
5812   DM_Plex *mesh = (DM_Plex *)dm->data;
5813 
5814   PetscFunctionBegin;
5815   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5816   if (section) *section = mesh->supportSection;
5817   PetscFunctionReturn(PETSC_SUCCESS);
5818 }
5819 
5820 /*@C
5821   DMPlexGetCones - Return cone data
5822 
5823   Not Collective
5824 
5825   Input Parameter:
5826 . dm - The `DMPLEX` object
5827 
5828   Output Parameter:
5829 . cones - The cone for each point
5830 
5831   Level: developer
5832 
5833 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5834 @*/
5835 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5836 {
5837   DM_Plex *mesh = (DM_Plex *)dm->data;
5838 
5839   PetscFunctionBegin;
5840   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5841   if (cones) *cones = mesh->cones;
5842   PetscFunctionReturn(PETSC_SUCCESS);
5843 }
5844 
5845 /*@C
5846   DMPlexGetConeOrientations - Return cone orientation data
5847 
5848   Not Collective
5849 
5850   Input Parameter:
5851 . dm - The `DMPLEX` object
5852 
5853   Output Parameter:
5854 . coneOrientations - The array of cone orientations for all points
5855 
5856   Level: developer
5857 
5858   Notes:
5859   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points
5860   as returned by `DMPlexGetConeOrientation()`.
5861 
5862   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5863 
5864 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5865 @*/
5866 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5867 {
5868   DM_Plex *mesh = (DM_Plex *)dm->data;
5869 
5870   PetscFunctionBegin;
5871   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5872   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5873   PetscFunctionReturn(PETSC_SUCCESS);
5874 }
5875 
5876 /* FEM Support */
5877 
5878 PetscErrorCode DMPlexGetAllCells_Internal(DM plex, IS *cellIS)
5879 {
5880   PetscInt depth;
5881 
5882   PetscFunctionBegin;
5883   PetscCall(DMPlexGetDepth(plex, &depth));
5884   PetscCall(DMGetStratumIS(plex, "dim", depth, cellIS));
5885   if (!*cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, cellIS));
5886   PetscFunctionReturn(PETSC_SUCCESS);
5887 }
5888 
5889 PetscErrorCode DMPlexGetAllFaces_Internal(DM plex, IS *faceIS)
5890 {
5891   PetscInt depth;
5892 
5893   PetscFunctionBegin;
5894   PetscCall(DMPlexGetDepth(plex, &depth));
5895   PetscCall(DMGetStratumIS(plex, "dim", depth - 1, faceIS));
5896   if (!*faceIS) PetscCall(DMGetStratumIS(plex, "depth", depth - 1, faceIS));
5897   PetscFunctionReturn(PETSC_SUCCESS);
5898 }
5899 
5900 /*
5901  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5902  representing a line in the section.
5903 */
5904 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(DM dm, PetscSection section, PetscInt field, PetscInt line, PetscInt *Nc, PetscInt *k, PetscBool *continuous, PetscBool *tensor)
5905 {
5906   PetscObject  obj;
5907   PetscClassId id;
5908   PetscFE      fe = NULL;
5909 
5910   PetscFunctionBeginHot;
5911   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5912   PetscCall(DMGetField(dm, field, NULL, &obj));
5913   PetscCall(PetscObjectGetClassId(obj, &id));
5914   if (id == PETSCFE_CLASSID) fe = (PetscFE)obj;
5915 
5916   if (!fe) {
5917     /* Assume the full interpolated mesh is in the chart; lines in particular */
5918     /* An order k SEM disc has k-1 dofs on an edge */
5919     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5920     *k = *k / *Nc + 1;
5921   } else {
5922     PetscInt       dual_space_size, dim;
5923     PetscDualSpace dsp;
5924 
5925     PetscCall(DMGetDimension(dm, &dim));
5926     PetscCall(PetscFEGetDualSpace(fe, &dsp));
5927     PetscCall(PetscDualSpaceGetDimension(dsp, &dual_space_size));
5928     *k = (PetscInt)PetscCeilReal(PetscPowReal(dual_space_size / *Nc, 1.0 / dim)) - 1;
5929     PetscCall(PetscDualSpaceLagrangeGetContinuity(dsp, continuous));
5930     PetscCall(PetscDualSpaceLagrangeGetTensor(dsp, tensor));
5931   }
5932   PetscFunctionReturn(PETSC_SUCCESS);
5933 }
5934 
5935 static PetscErrorCode GetFieldSize_Private(PetscInt dim, PetscInt k, PetscBool tensor, PetscInt *dof)
5936 {
5937   PetscFunctionBeginHot;
5938   if (tensor) {
5939     *dof = PetscPowInt(k + 1, dim);
5940   } else {
5941     switch (dim) {
5942     case 1:
5943       *dof = k + 1;
5944       break;
5945     case 2:
5946       *dof = ((k + 1) * (k + 2)) / 2;
5947       break;
5948     case 3:
5949       *dof = ((k + 1) * (k + 2) * (k + 3)) / 6;
5950       break;
5951     default:
5952       *dof = 0;
5953     }
5954   }
5955   PetscFunctionReturn(PETSC_SUCCESS);
5956 }
5957 
5958 /*@
5959   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5960   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5961   section provided (or the section of the `DM`).
5962 
5963   Input Parameters:
5964 + dm      - The `DM`
5965 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5966 - section - The `PetscSection` to reorder, or `NULL` for the default section
5967 
5968   Example:
5969   A typical interpolated single-quad mesh might order points as
5970 .vb
5971   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5972 
5973   v4 -- e6 -- v3
5974   |           |
5975   e7    c0    e8
5976   |           |
5977   v1 -- e5 -- v2
5978 .ve
5979 
5980   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5981   dofs in the order of points, e.g.,
5982 .vb
5983     c0 -> [0,1,2,3]
5984     v1 -> [4]
5985     ...
5986     e5 -> [8, 9]
5987 .ve
5988 
5989   which corresponds to the dofs
5990 .vb
5991     6   10  11  7
5992     13  2   3   15
5993     12  0   1   14
5994     4   8   9   5
5995 .ve
5996 
5997   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5998 .vb
5999   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
6000 .ve
6001 
6002   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
6003 .vb
6004    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
6005 .ve
6006 
6007   Level: developer
6008 
6009   Notes:
6010   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
6011   degree of the basis.
6012 
6013   This is required to run with libCEED.
6014 
6015 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
6016 @*/
6017 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
6018 {
6019   DMLabel   label;
6020   PetscInt  dim, depth = -1, eStart = -1, Nf;
6021   PetscBool continuous = PETSC_TRUE, tensor = PETSC_TRUE;
6022 
6023   PetscFunctionBegin;
6024   PetscCall(DMGetDimension(dm, &dim));
6025   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
6026   if (point < 0) {
6027     PetscInt sStart, sEnd;
6028 
6029     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
6030     point = sEnd - sStart ? sStart : point;
6031   }
6032   PetscCall(DMPlexGetDepthLabel(dm, &label));
6033   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
6034   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6035   if (depth == 1) {
6036     eStart = point;
6037   } else if (depth == dim) {
6038     const PetscInt *cone;
6039 
6040     PetscCall(DMPlexGetCone(dm, point, &cone));
6041     if (dim == 2) eStart = cone[0];
6042     else if (dim == 3) {
6043       const PetscInt *cone2;
6044       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
6045       eStart = cone2[0];
6046     } 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);
6047   } 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);
6048 
6049   PetscCall(PetscSectionGetNumFields(section, &Nf));
6050   for (PetscInt d = 1; d <= dim; d++) {
6051     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
6052     PetscInt *perm;
6053 
6054     for (f = 0; f < Nf; ++f) {
6055       PetscInt dof;
6056 
6057       PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6058       PetscCheck(dim == 1 || tensor || !continuous, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Continuous field %" PetscInt_FMT " must have a tensor product discretization", f);
6059       if (!continuous && d < dim) continue;
6060       PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6061       size += dof * Nc;
6062     }
6063     PetscCall(PetscMalloc1(size, &perm));
6064     for (f = 0; f < Nf; ++f) {
6065       switch (d) {
6066       case 1:
6067         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6068         if (!continuous && d < dim) continue;
6069         /*
6070          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
6071          We want              [ vtx0; edge of length k-1; vtx1 ]
6072          */
6073         if (continuous) {
6074           for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
6075           for (i = 0; i < k - 1; i++)
6076             for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
6077           for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
6078           foffset = offset;
6079         } else {
6080           PetscInt dof;
6081 
6082           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6083           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6084           foffset = offset;
6085         }
6086         break;
6087       case 2:
6088         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
6089         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6090         if (!continuous && d < dim) continue;
6091         /* The SEM order is
6092 
6093          v_lb, {e_b}, v_rb,
6094          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
6095          v_lt, reverse {e_t}, v_rt
6096          */
6097         if (continuous) {
6098           const PetscInt of   = 0;
6099           const PetscInt oeb  = of + PetscSqr(k - 1);
6100           const PetscInt oer  = oeb + (k - 1);
6101           const PetscInt oet  = oer + (k - 1);
6102           const PetscInt oel  = oet + (k - 1);
6103           const PetscInt ovlb = oel + (k - 1);
6104           const PetscInt ovrb = ovlb + 1;
6105           const PetscInt ovrt = ovrb + 1;
6106           const PetscInt ovlt = ovrt + 1;
6107           PetscInt       o;
6108 
6109           /* bottom */
6110           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
6111           for (o = oeb; o < oer; ++o)
6112             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6113           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
6114           /* middle */
6115           for (i = 0; i < k - 1; ++i) {
6116             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
6117             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
6118               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6119             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
6120           }
6121           /* top */
6122           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
6123           for (o = oel - 1; o >= oet; --o)
6124             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6125           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
6126           foffset = offset;
6127         } else {
6128           PetscInt dof;
6129 
6130           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6131           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6132           foffset = offset;
6133         }
6134         break;
6135       case 3:
6136         /* The original hex closure is
6137 
6138          {c,
6139          f_b, f_t, f_f, f_b, f_r, f_l,
6140          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
6141          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
6142          */
6143         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6144         if (!continuous && d < dim) continue;
6145         /* The SEM order is
6146          Bottom Slice
6147          v_blf, {e^{(k-1)-n}_bf}, v_brf,
6148          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
6149          v_blb, {e_bb}, v_brb,
6150 
6151          Middle Slice (j)
6152          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
6153          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
6154          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
6155 
6156          Top Slice
6157          v_tlf, {e_tf}, v_trf,
6158          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
6159          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
6160          */
6161         if (continuous) {
6162           const PetscInt oc    = 0;
6163           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
6164           const PetscInt oft   = ofb + PetscSqr(k - 1);
6165           const PetscInt off   = oft + PetscSqr(k - 1);
6166           const PetscInt ofk   = off + PetscSqr(k - 1);
6167           const PetscInt ofr   = ofk + PetscSqr(k - 1);
6168           const PetscInt ofl   = ofr + PetscSqr(k - 1);
6169           const PetscInt oebl  = ofl + PetscSqr(k - 1);
6170           const PetscInt oebb  = oebl + (k - 1);
6171           const PetscInt oebr  = oebb + (k - 1);
6172           const PetscInt oebf  = oebr + (k - 1);
6173           const PetscInt oetf  = oebf + (k - 1);
6174           const PetscInt oetr  = oetf + (k - 1);
6175           const PetscInt oetb  = oetr + (k - 1);
6176           const PetscInt oetl  = oetb + (k - 1);
6177           const PetscInt oerf  = oetl + (k - 1);
6178           const PetscInt oelf  = oerf + (k - 1);
6179           const PetscInt oelb  = oelf + (k - 1);
6180           const PetscInt oerb  = oelb + (k - 1);
6181           const PetscInt ovblf = oerb + (k - 1);
6182           const PetscInt ovblb = ovblf + 1;
6183           const PetscInt ovbrb = ovblb + 1;
6184           const PetscInt ovbrf = ovbrb + 1;
6185           const PetscInt ovtlf = ovbrf + 1;
6186           const PetscInt ovtrf = ovtlf + 1;
6187           const PetscInt ovtrb = ovtrf + 1;
6188           const PetscInt ovtlb = ovtrb + 1;
6189           PetscInt       o, n;
6190 
6191           /* Bottom Slice */
6192           /*   bottom */
6193           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
6194           for (o = oetf - 1; o >= oebf; --o)
6195             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6196           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
6197           /*   middle */
6198           for (i = 0; i < k - 1; ++i) {
6199             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
6200             for (n = 0; n < k - 1; ++n) {
6201               o = ofb + n * (k - 1) + i;
6202               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6203             }
6204             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
6205           }
6206           /*   top */
6207           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
6208           for (o = oebb; o < oebr; ++o)
6209             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6210           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
6211 
6212           /* Middle Slice */
6213           for (j = 0; j < k - 1; ++j) {
6214             /*   bottom */
6215             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
6216             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
6217               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6218             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
6219             /*   middle */
6220             for (i = 0; i < k - 1; ++i) {
6221               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
6222               for (n = 0; n < k - 1; ++n)
6223                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
6224               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
6225             }
6226             /*   top */
6227             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
6228             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
6229               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6230             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
6231           }
6232 
6233           /* Top Slice */
6234           /*   bottom */
6235           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
6236           for (o = oetf; o < oetr; ++o)
6237             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6238           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
6239           /*   middle */
6240           for (i = 0; i < k - 1; ++i) {
6241             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
6242             for (n = 0; n < k - 1; ++n)
6243               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
6244             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
6245           }
6246           /*   top */
6247           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
6248           for (o = oetl - 1; o >= oetb; --o)
6249             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6250           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
6251 
6252           foffset = offset;
6253         } else {
6254           PetscInt dof;
6255 
6256           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6257           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6258           foffset = offset;
6259         }
6260         break;
6261       default:
6262         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
6263       }
6264     }
6265     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
6266     /* Check permutation */
6267     {
6268       PetscInt *check;
6269 
6270       PetscCall(PetscMalloc1(size, &check));
6271       for (i = 0; i < size; ++i) {
6272         check[i] = -1;
6273         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
6274       }
6275       for (i = 0; i < size; ++i) check[perm[i]] = i;
6276       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
6277       PetscCall(PetscFree(check));
6278     }
6279     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
6280     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
6281       PetscInt *loc_perm;
6282       PetscCall(PetscMalloc1(size * 2, &loc_perm));
6283       for (PetscInt i = 0; i < size; i++) {
6284         loc_perm[i]        = perm[i];
6285         loc_perm[size + i] = size + perm[i];
6286       }
6287       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
6288     }
6289   }
6290   PetscFunctionReturn(PETSC_SUCCESS);
6291 }
6292 
6293 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
6294 {
6295   PetscDS  prob;
6296   PetscInt depth, Nf, h;
6297   DMLabel  label;
6298 
6299   PetscFunctionBeginHot;
6300   PetscCall(DMGetDS(dm, &prob));
6301   Nf      = prob->Nf;
6302   label   = dm->depthLabel;
6303   *dspace = NULL;
6304   if (field < Nf) {
6305     PetscObject disc = prob->disc[field];
6306 
6307     if (disc->classid == PETSCFE_CLASSID) {
6308       PetscDualSpace dsp;
6309 
6310       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
6311       PetscCall(DMLabelGetNumValues(label, &depth));
6312       PetscCall(DMLabelGetValue(label, point, &h));
6313       h = depth - 1 - h;
6314       if (h) {
6315         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
6316       } else {
6317         *dspace = dsp;
6318       }
6319     }
6320   }
6321   PetscFunctionReturn(PETSC_SUCCESS);
6322 }
6323 
6324 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6325 {
6326   PetscScalar       *array;
6327   const PetscScalar *vArray;
6328   const PetscInt    *cone, *coneO;
6329   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
6330 
6331   PetscFunctionBeginHot;
6332   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6333   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6334   PetscCall(DMPlexGetCone(dm, point, &cone));
6335   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6336   if (!values || !*values) {
6337     if ((point >= pStart) && (point < pEnd)) {
6338       PetscInt dof;
6339 
6340       PetscCall(PetscSectionGetDof(section, point, &dof));
6341       size += dof;
6342     }
6343     for (p = 0; p < numPoints; ++p) {
6344       const PetscInt cp = cone[p];
6345       PetscInt       dof;
6346 
6347       if ((cp < pStart) || (cp >= pEnd)) continue;
6348       PetscCall(PetscSectionGetDof(section, cp, &dof));
6349       size += dof;
6350     }
6351     if (!values) {
6352       if (csize) *csize = size;
6353       PetscFunctionReturn(PETSC_SUCCESS);
6354     }
6355     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
6356   } else {
6357     array = *values;
6358   }
6359   size = 0;
6360   PetscCall(VecGetArrayRead(v, &vArray));
6361   if ((point >= pStart) && (point < pEnd)) {
6362     PetscInt           dof, off, d;
6363     const PetscScalar *varr;
6364 
6365     PetscCall(PetscSectionGetDof(section, point, &dof));
6366     PetscCall(PetscSectionGetOffset(section, point, &off));
6367     varr = PetscSafePointerPlusOffset(vArray, off);
6368     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6369     size += dof;
6370   }
6371   for (p = 0; p < numPoints; ++p) {
6372     const PetscInt     cp = cone[p];
6373     PetscInt           o  = coneO[p];
6374     PetscInt           dof, off, d;
6375     const PetscScalar *varr;
6376 
6377     if ((cp < pStart) || (cp >= pEnd)) continue;
6378     PetscCall(PetscSectionGetDof(section, cp, &dof));
6379     PetscCall(PetscSectionGetOffset(section, cp, &off));
6380     varr = PetscSafePointerPlusOffset(vArray, off);
6381     if (o >= 0) {
6382       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6383     } else {
6384       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
6385     }
6386     size += dof;
6387   }
6388   PetscCall(VecRestoreArrayRead(v, &vArray));
6389   if (!*values) {
6390     if (csize) *csize = size;
6391     *values = array;
6392   } else {
6393     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6394     *csize = size;
6395   }
6396   PetscFunctionReturn(PETSC_SUCCESS);
6397 }
6398 
6399 /* Compress out points not in the section */
6400 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
6401 {
6402   const PetscInt np = *numPoints;
6403   PetscInt       pStart, pEnd, p, q;
6404 
6405   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6406   for (p = 0, q = 0; p < np; ++p) {
6407     const PetscInt r = points[p * 2];
6408     if ((r >= pStart) && (r < pEnd)) {
6409       points[q * 2]     = r;
6410       points[q * 2 + 1] = points[p * 2 + 1];
6411       ++q;
6412     }
6413   }
6414   *numPoints = q;
6415   return PETSC_SUCCESS;
6416 }
6417 
6418 /* Compressed closure does not apply closure permutation */
6419 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6420 {
6421   const PetscInt *cla = NULL;
6422   PetscInt        np, *pts = NULL;
6423 
6424   PetscFunctionBeginHot;
6425   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
6426   if (!ornt && *clPoints) {
6427     PetscInt dof, off;
6428 
6429     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
6430     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
6431     PetscCall(ISGetIndices(*clPoints, &cla));
6432     np  = dof / 2;
6433     pts = PetscSafePointerPlusOffset((PetscInt *)cla, off);
6434   } else {
6435     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
6436     PetscCall(CompressPoints_Private(section, &np, pts));
6437   }
6438   *numPoints = np;
6439   *points    = pts;
6440   *clp       = cla;
6441   PetscFunctionReturn(PETSC_SUCCESS);
6442 }
6443 
6444 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6445 {
6446   PetscFunctionBeginHot;
6447   if (!*clPoints) {
6448     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
6449   } else {
6450     PetscCall(ISRestoreIndices(*clPoints, clp));
6451   }
6452   *numPoints = 0;
6453   *points    = NULL;
6454   *clSec     = NULL;
6455   *clPoints  = NULL;
6456   *clp       = NULL;
6457   PetscFunctionReturn(PETSC_SUCCESS);
6458 }
6459 
6460 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
6461 {
6462   PetscInt            offset = 0, p;
6463   const PetscInt    **perms  = NULL;
6464   const PetscScalar **flips  = NULL;
6465 
6466   PetscFunctionBeginHot;
6467   *size = 0;
6468   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6469   for (p = 0; p < numPoints; p++) {
6470     const PetscInt     point = points[2 * p];
6471     const PetscInt    *perm  = perms ? perms[p] : NULL;
6472     const PetscScalar *flip  = flips ? flips[p] : NULL;
6473     PetscInt           dof, off, d;
6474     const PetscScalar *varr;
6475 
6476     PetscCall(PetscSectionGetDof(section, point, &dof));
6477     PetscCall(PetscSectionGetOffset(section, point, &off));
6478     varr = PetscSafePointerPlusOffset(vArray, off);
6479     if (clperm) {
6480       if (perm) {
6481         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
6482       } else {
6483         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
6484       }
6485       if (flip) {
6486         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
6487       }
6488     } else {
6489       if (perm) {
6490         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
6491       } else {
6492         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
6493       }
6494       if (flip) {
6495         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
6496       }
6497     }
6498     offset += dof;
6499   }
6500   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6501   *size = offset;
6502   PetscFunctionReturn(PETSC_SUCCESS);
6503 }
6504 
6505 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[])
6506 {
6507   PetscInt offset = 0, f;
6508 
6509   PetscFunctionBeginHot;
6510   *size = 0;
6511   for (f = 0; f < numFields; ++f) {
6512     PetscInt            p;
6513     const PetscInt    **perms = NULL;
6514     const PetscScalar **flips = NULL;
6515 
6516     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6517     for (p = 0; p < numPoints; p++) {
6518       const PetscInt     point = points[2 * p];
6519       PetscInt           fdof, foff, b;
6520       const PetscScalar *varr;
6521       const PetscInt    *perm = perms ? perms[p] : NULL;
6522       const PetscScalar *flip = flips ? flips[p] : NULL;
6523 
6524       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6525       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6526       varr = &vArray[foff];
6527       if (clperm) {
6528         if (perm) {
6529           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6530         } else {
6531           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6532         }
6533         if (flip) {
6534           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6535         }
6536       } else {
6537         if (perm) {
6538           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6539         } else {
6540           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6541         }
6542         if (flip) {
6543           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6544         }
6545       }
6546       offset += fdof;
6547     }
6548     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6549   }
6550   *size = offset;
6551   PetscFunctionReturn(PETSC_SUCCESS);
6552 }
6553 
6554 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, PetscBool useClPerm, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6555 {
6556   PetscSection    clSection;
6557   IS              clPoints;
6558   PetscInt       *points = NULL;
6559   const PetscInt *clp, *perm = NULL;
6560   PetscInt        depth, numFields, numPoints, asize;
6561 
6562   PetscFunctionBeginHot;
6563   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6564   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6565   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6566   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
6567   PetscCall(DMPlexGetDepth(dm, &depth));
6568   PetscCall(PetscSectionGetNumFields(section, &numFields));
6569   if (depth == 1 && numFields < 2) {
6570     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6571     PetscFunctionReturn(PETSC_SUCCESS);
6572   }
6573   /* Get points */
6574   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6575   /* Get sizes */
6576   asize = 0;
6577   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6578     PetscInt dof;
6579     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6580     asize += dof;
6581   }
6582   if (values) {
6583     const PetscScalar *vArray;
6584     PetscInt           size;
6585 
6586     if (*values) {
6587       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);
6588     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6589     if (useClPerm) PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6590     PetscCall(VecGetArrayRead(v, &vArray));
6591     /* Get values */
6592     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6593     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6594     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6595     /* Cleanup array */
6596     PetscCall(VecRestoreArrayRead(v, &vArray));
6597   }
6598   if (csize) *csize = asize;
6599   /* Cleanup points */
6600   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6601   PetscFunctionReturn(PETSC_SUCCESS);
6602 }
6603 
6604 /*@C
6605   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6606 
6607   Not collective
6608 
6609   Input Parameters:
6610 + dm      - The `DM`
6611 . section - The section describing the layout in `v`, or `NULL` to use the default section
6612 . v       - The local vector
6613 - point   - The point in the `DM`
6614 
6615   Input/Output Parameters:
6616 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6617 - values - An array to use for the values, or *values = `NULL` to have it allocated automatically;
6618            if the user provided `NULL`, it is a borrowed array and should not be freed, use  `DMPlexVecRestoreClosure()` to return it
6619 
6620   Level: intermediate
6621 
6622   Notes:
6623   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6624   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6625   assembly function, and a user may already have allocated storage for this operation.
6626 
6627   A typical use could be
6628 .vb
6629    values = NULL;
6630    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6631    for (cl = 0; cl < clSize; ++cl) {
6632      <Compute on closure>
6633    }
6634    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6635 .ve
6636   or
6637 .vb
6638    PetscMalloc1(clMaxSize, &values);
6639    for (p = pStart; p < pEnd; ++p) {
6640      clSize = clMaxSize;
6641      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6642      for (cl = 0; cl < clSize; ++cl) {
6643        <Compute on closure>
6644      }
6645    }
6646    PetscFree(values);
6647 .ve
6648 
6649   Fortran Notes:
6650   The `csize` argument is present in the Fortran binding. Since the Fortran `values` array contains its length information this argument may not be needed.
6651   In that case one may pass `PETSC_NULL_INTEGER` for `csize`.
6652 
6653   `values` must be declared with
6654 .vb
6655   PetscScalar,dimension(:),pointer   :: values
6656 .ve
6657   and it will be allocated internally by PETSc to hold the values returned
6658 
6659 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6660 @*/
6661 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6662 {
6663   PetscFunctionBeginHot;
6664   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6665   PetscFunctionReturn(PETSC_SUCCESS);
6666 }
6667 
6668 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6669 {
6670   DMLabel            depthLabel;
6671   PetscSection       clSection;
6672   IS                 clPoints;
6673   PetscScalar       *array;
6674   const PetscScalar *vArray;
6675   PetscInt          *points = NULL;
6676   const PetscInt    *clp, *perm = NULL;
6677   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6678 
6679   PetscFunctionBeginHot;
6680   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6681   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6682   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6683   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6684   PetscCall(DMPlexGetDepth(dm, &mdepth));
6685   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6686   PetscCall(PetscSectionGetNumFields(section, &numFields));
6687   if (mdepth == 1 && numFields < 2) {
6688     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6689     PetscFunctionReturn(PETSC_SUCCESS);
6690   }
6691   /* Get points */
6692   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6693   for (clsize = 0, p = 0; p < Np; p++) {
6694     PetscInt dof;
6695     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6696     clsize += dof;
6697   }
6698   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6699   /* Filter points */
6700   for (p = 0; p < numPoints * 2; p += 2) {
6701     PetscInt dep;
6702 
6703     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6704     if (dep != depth) continue;
6705     points[Np * 2 + 0] = points[p];
6706     points[Np * 2 + 1] = points[p + 1];
6707     ++Np;
6708   }
6709   /* Get array */
6710   if (!values || !*values) {
6711     PetscInt asize = 0, dof;
6712 
6713     for (p = 0; p < Np * 2; p += 2) {
6714       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6715       asize += dof;
6716     }
6717     if (!values) {
6718       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6719       if (csize) *csize = asize;
6720       PetscFunctionReturn(PETSC_SUCCESS);
6721     }
6722     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6723   } else {
6724     array = *values;
6725   }
6726   PetscCall(VecGetArrayRead(v, &vArray));
6727   /* Get values */
6728   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6729   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6730   /* Cleanup points */
6731   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6732   /* Cleanup array */
6733   PetscCall(VecRestoreArrayRead(v, &vArray));
6734   if (!*values) {
6735     if (csize) *csize = size;
6736     *values = array;
6737   } else {
6738     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6739     *csize = size;
6740   }
6741   PetscFunctionReturn(PETSC_SUCCESS);
6742 }
6743 
6744 /*@C
6745   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point' obtained with `DMPlexVecGetClosure()`
6746 
6747   Not collective
6748 
6749   Input Parameters:
6750 + dm      - The `DM`
6751 . section - The section describing the layout in `v`, or `NULL` to use the default section
6752 . v       - The local vector
6753 . point   - The point in the `DM`
6754 . csize   - The number of values in the closure, or `NULL`
6755 - values  - The array of values
6756 
6757   Level: intermediate
6758 
6759   Note:
6760   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6761 
6762   Fortran Note:
6763   The `csize` argument is present in the Fortran binding. Since the Fortran `values` array contains its length information this argument may not be needed.
6764   In that case one may pass `PETSC_NULL_INTEGER` for `csize`.
6765 
6766 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6767 @*/
6768 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6769 {
6770   PetscInt size = 0;
6771 
6772   PetscFunctionBegin;
6773   /* Should work without recalculating size */
6774   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6775   *values = NULL;
6776   PetscFunctionReturn(PETSC_SUCCESS);
6777 }
6778 
6779 static inline void add(PetscScalar *x, PetscScalar y)
6780 {
6781   *x += y;
6782 }
6783 static inline void insert(PetscScalar *x, PetscScalar y)
6784 {
6785   *x = y;
6786 }
6787 
6788 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[])
6789 {
6790   PetscInt        cdof;  /* The number of constraints on this point */
6791   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6792   PetscScalar    *a;
6793   PetscInt        off, cind = 0, k;
6794 
6795   PetscFunctionBegin;
6796   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6797   PetscCall(PetscSectionGetOffset(section, point, &off));
6798   a = &array[off];
6799   if (!cdof || setBC) {
6800     if (clperm) {
6801       if (perm) {
6802         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6803       } else {
6804         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6805       }
6806     } else {
6807       if (perm) {
6808         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6809       } else {
6810         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6811       }
6812     }
6813   } else {
6814     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6815     if (clperm) {
6816       if (perm) {
6817         for (k = 0; k < dof; ++k) {
6818           if ((cind < cdof) && (k == cdofs[cind])) {
6819             ++cind;
6820             continue;
6821           }
6822           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6823         }
6824       } else {
6825         for (k = 0; k < dof; ++k) {
6826           if ((cind < cdof) && (k == cdofs[cind])) {
6827             ++cind;
6828             continue;
6829           }
6830           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6831         }
6832       }
6833     } else {
6834       if (perm) {
6835         for (k = 0; k < dof; ++k) {
6836           if ((cind < cdof) && (k == cdofs[cind])) {
6837             ++cind;
6838             continue;
6839           }
6840           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6841         }
6842       } else {
6843         for (k = 0; k < dof; ++k) {
6844           if ((cind < cdof) && (k == cdofs[cind])) {
6845             ++cind;
6846             continue;
6847           }
6848           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6849         }
6850       }
6851     }
6852   }
6853   PetscFunctionReturn(PETSC_SUCCESS);
6854 }
6855 
6856 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[])
6857 {
6858   PetscInt        cdof;  /* The number of constraints on this point */
6859   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6860   PetscScalar    *a;
6861   PetscInt        off, cind = 0, k;
6862 
6863   PetscFunctionBegin;
6864   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6865   PetscCall(PetscSectionGetOffset(section, point, &off));
6866   a = &array[off];
6867   if (cdof) {
6868     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6869     if (clperm) {
6870       if (perm) {
6871         for (k = 0; k < dof; ++k) {
6872           if ((cind < cdof) && (k == cdofs[cind])) {
6873             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6874             cind++;
6875           }
6876         }
6877       } else {
6878         for (k = 0; k < dof; ++k) {
6879           if ((cind < cdof) && (k == cdofs[cind])) {
6880             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6881             cind++;
6882           }
6883         }
6884       }
6885     } else {
6886       if (perm) {
6887         for (k = 0; k < dof; ++k) {
6888           if ((cind < cdof) && (k == cdofs[cind])) {
6889             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6890             cind++;
6891           }
6892         }
6893       } else {
6894         for (k = 0; k < dof; ++k) {
6895           if ((cind < cdof) && (k == cdofs[cind])) {
6896             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6897             cind++;
6898           }
6899         }
6900       }
6901     }
6902   }
6903   PetscFunctionReturn(PETSC_SUCCESS);
6904 }
6905 
6906 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[])
6907 {
6908   PetscScalar    *a;
6909   PetscInt        fdof, foff, fcdof, foffset = *offset;
6910   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6911   PetscInt        cind = 0, b;
6912 
6913   PetscFunctionBegin;
6914   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6915   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6916   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6917   a = &array[foff];
6918   if (!fcdof || setBC) {
6919     if (clperm) {
6920       if (perm) {
6921         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6922       } else {
6923         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6924       }
6925     } else {
6926       if (perm) {
6927         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6928       } else {
6929         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6930       }
6931     }
6932   } else {
6933     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6934     if (clperm) {
6935       if (perm) {
6936         for (b = 0; b < fdof; b++) {
6937           if ((cind < fcdof) && (b == fcdofs[cind])) {
6938             ++cind;
6939             continue;
6940           }
6941           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6942         }
6943       } else {
6944         for (b = 0; b < fdof; b++) {
6945           if ((cind < fcdof) && (b == fcdofs[cind])) {
6946             ++cind;
6947             continue;
6948           }
6949           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6950         }
6951       }
6952     } else {
6953       if (perm) {
6954         for (b = 0; b < fdof; b++) {
6955           if ((cind < fcdof) && (b == fcdofs[cind])) {
6956             ++cind;
6957             continue;
6958           }
6959           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6960         }
6961       } else {
6962         for (b = 0; b < fdof; b++) {
6963           if ((cind < fcdof) && (b == fcdofs[cind])) {
6964             ++cind;
6965             continue;
6966           }
6967           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6968         }
6969       }
6970     }
6971   }
6972   *offset += fdof;
6973   PetscFunctionReturn(PETSC_SUCCESS);
6974 }
6975 
6976 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[])
6977 {
6978   PetscScalar    *a;
6979   PetscInt        fdof, foff, fcdof, foffset = *offset;
6980   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6981   PetscInt        Nc, cind = 0, ncind = 0, b;
6982   PetscBool       ncSet, fcSet;
6983 
6984   PetscFunctionBegin;
6985   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6986   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6987   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6988   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6989   a = &array[foff];
6990   if (fcdof) {
6991     /* We just override fcdof and fcdofs with Ncc and comps */
6992     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6993     if (clperm) {
6994       if (perm) {
6995         if (comps) {
6996           for (b = 0; b < fdof; b++) {
6997             ncSet = fcSet = PETSC_FALSE;
6998             if (b % Nc == comps[ncind]) {
6999               ncind = (ncind + 1) % Ncc;
7000               ncSet = PETSC_TRUE;
7001             }
7002             if ((cind < fcdof) && (b == fcdofs[cind])) {
7003               ++cind;
7004               fcSet = PETSC_TRUE;
7005             }
7006             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
7007           }
7008         } else {
7009           for (b = 0; b < fdof; b++) {
7010             if ((cind < fcdof) && (b == fcdofs[cind])) {
7011               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
7012               ++cind;
7013             }
7014           }
7015         }
7016       } else {
7017         if (comps) {
7018           for (b = 0; b < fdof; b++) {
7019             ncSet = fcSet = PETSC_FALSE;
7020             if (b % Nc == comps[ncind]) {
7021               ncind = (ncind + 1) % Ncc;
7022               ncSet = PETSC_TRUE;
7023             }
7024             if ((cind < fcdof) && (b == fcdofs[cind])) {
7025               ++cind;
7026               fcSet = PETSC_TRUE;
7027             }
7028             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
7029           }
7030         } else {
7031           for (b = 0; b < fdof; b++) {
7032             if ((cind < fcdof) && (b == fcdofs[cind])) {
7033               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
7034               ++cind;
7035             }
7036           }
7037         }
7038       }
7039     } else {
7040       if (perm) {
7041         if (comps) {
7042           for (b = 0; b < fdof; b++) {
7043             ncSet = fcSet = PETSC_FALSE;
7044             if (b % Nc == comps[ncind]) {
7045               ncind = (ncind + 1) % Ncc;
7046               ncSet = PETSC_TRUE;
7047             }
7048             if ((cind < fcdof) && (b == fcdofs[cind])) {
7049               ++cind;
7050               fcSet = PETSC_TRUE;
7051             }
7052             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
7053           }
7054         } else {
7055           for (b = 0; b < fdof; b++) {
7056             if ((cind < fcdof) && (b == fcdofs[cind])) {
7057               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
7058               ++cind;
7059             }
7060           }
7061         }
7062       } else {
7063         if (comps) {
7064           for (b = 0; b < fdof; b++) {
7065             ncSet = fcSet = PETSC_FALSE;
7066             if (b % Nc == comps[ncind]) {
7067               ncind = (ncind + 1) % Ncc;
7068               ncSet = PETSC_TRUE;
7069             }
7070             if ((cind < fcdof) && (b == fcdofs[cind])) {
7071               ++cind;
7072               fcSet = PETSC_TRUE;
7073             }
7074             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
7075           }
7076         } else {
7077           for (b = 0; b < fdof; b++) {
7078             if ((cind < fcdof) && (b == fcdofs[cind])) {
7079               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
7080               ++cind;
7081             }
7082           }
7083         }
7084       }
7085     }
7086   }
7087   *offset += fdof;
7088   PetscFunctionReturn(PETSC_SUCCESS);
7089 }
7090 
7091 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7092 {
7093   PetscScalar    *array;
7094   const PetscInt *cone, *coneO;
7095   PetscInt        pStart, pEnd, p, numPoints, off, dof;
7096 
7097   PetscFunctionBeginHot;
7098   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
7099   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
7100   PetscCall(DMPlexGetCone(dm, point, &cone));
7101   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
7102   PetscCall(VecGetArray(v, &array));
7103   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
7104     const PetscInt cp = !p ? point : cone[p - 1];
7105     const PetscInt o  = !p ? 0 : coneO[p - 1];
7106 
7107     if ((cp < pStart) || (cp >= pEnd)) {
7108       dof = 0;
7109       continue;
7110     }
7111     PetscCall(PetscSectionGetDof(section, cp, &dof));
7112     /* ADD_VALUES */
7113     {
7114       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7115       PetscScalar    *a;
7116       PetscInt        cdof, coff, cind = 0, k;
7117 
7118       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
7119       PetscCall(PetscSectionGetOffset(section, cp, &coff));
7120       a = &array[coff];
7121       if (!cdof) {
7122         if (o >= 0) {
7123           for (k = 0; k < dof; ++k) a[k] += values[off + k];
7124         } else {
7125           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
7126         }
7127       } else {
7128         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
7129         if (o >= 0) {
7130           for (k = 0; k < dof; ++k) {
7131             if ((cind < cdof) && (k == cdofs[cind])) {
7132               ++cind;
7133               continue;
7134             }
7135             a[k] += values[off + k];
7136           }
7137         } else {
7138           for (k = 0; k < dof; ++k) {
7139             if ((cind < cdof) && (k == cdofs[cind])) {
7140               ++cind;
7141               continue;
7142             }
7143             a[k] += values[off + dof - k - 1];
7144           }
7145         }
7146       }
7147     }
7148   }
7149   PetscCall(VecRestoreArray(v, &array));
7150   PetscFunctionReturn(PETSC_SUCCESS);
7151 }
7152 
7153 /*@C
7154   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
7155 
7156   Not collective
7157 
7158   Input Parameters:
7159 + dm      - The `DM`
7160 . section - The section describing the layout in `v`, or `NULL` to use the default section
7161 . v       - The local vector
7162 . point   - The point in the `DM`
7163 . values  - The array of values
7164 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
7165             where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
7166 
7167   Level: intermediate
7168 
7169   Note:
7170   Usually the input arrays were obtained with `DMPlexVecGetClosure()`
7171 
7172 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
7173 @*/
7174 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7175 {
7176   PetscSection    clSection;
7177   IS              clPoints;
7178   PetscScalar    *array;
7179   PetscInt       *points = NULL;
7180   const PetscInt *clp, *clperm = NULL;
7181   PetscInt        depth, numFields, numPoints, p, clsize;
7182 
7183   PetscFunctionBeginHot;
7184   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7185   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7186   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7187   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7188   PetscCall(DMPlexGetDepth(dm, &depth));
7189   PetscCall(PetscSectionGetNumFields(section, &numFields));
7190   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
7191     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
7192     PetscFunctionReturn(PETSC_SUCCESS);
7193   }
7194   /* Get points */
7195   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7196   for (clsize = 0, p = 0; p < numPoints; p++) {
7197     PetscInt dof;
7198     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7199     clsize += dof;
7200   }
7201   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7202   /* Get array */
7203   PetscCall(VecGetArray(v, &array));
7204   /* Get values */
7205   if (numFields > 0) {
7206     PetscInt offset = 0, f;
7207     for (f = 0; f < numFields; ++f) {
7208       const PetscInt    **perms = NULL;
7209       const PetscScalar **flips = NULL;
7210 
7211       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7212       switch (mode) {
7213       case INSERT_VALUES:
7214         for (p = 0; p < numPoints; p++) {
7215           const PetscInt     point = points[2 * p];
7216           const PetscInt    *perm  = perms ? perms[p] : NULL;
7217           const PetscScalar *flip  = flips ? flips[p] : NULL;
7218           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
7219         }
7220         break;
7221       case INSERT_ALL_VALUES:
7222         for (p = 0; p < numPoints; p++) {
7223           const PetscInt     point = points[2 * p];
7224           const PetscInt    *perm  = perms ? perms[p] : NULL;
7225           const PetscScalar *flip  = flips ? flips[p] : NULL;
7226           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
7227         }
7228         break;
7229       case INSERT_BC_VALUES:
7230         for (p = 0; p < numPoints; p++) {
7231           const PetscInt     point = points[2 * p];
7232           const PetscInt    *perm  = perms ? perms[p] : NULL;
7233           const PetscScalar *flip  = flips ? flips[p] : NULL;
7234           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
7235         }
7236         break;
7237       case ADD_VALUES:
7238         for (p = 0; p < numPoints; p++) {
7239           const PetscInt     point = points[2 * p];
7240           const PetscInt    *perm  = perms ? perms[p] : NULL;
7241           const PetscScalar *flip  = flips ? flips[p] : NULL;
7242           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
7243         }
7244         break;
7245       case ADD_ALL_VALUES:
7246         for (p = 0; p < numPoints; p++) {
7247           const PetscInt     point = points[2 * p];
7248           const PetscInt    *perm  = perms ? perms[p] : NULL;
7249           const PetscScalar *flip  = flips ? flips[p] : NULL;
7250           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
7251         }
7252         break;
7253       case ADD_BC_VALUES:
7254         for (p = 0; p < numPoints; p++) {
7255           const PetscInt     point = points[2 * p];
7256           const PetscInt    *perm  = perms ? perms[p] : NULL;
7257           const PetscScalar *flip  = flips ? flips[p] : NULL;
7258           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
7259         }
7260         break;
7261       default:
7262         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7263       }
7264       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7265     }
7266   } else {
7267     PetscInt            dof, off;
7268     const PetscInt    **perms = NULL;
7269     const PetscScalar **flips = NULL;
7270 
7271     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
7272     switch (mode) {
7273     case INSERT_VALUES:
7274       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7275         const PetscInt     point = points[2 * p];
7276         const PetscInt    *perm  = perms ? perms[p] : NULL;
7277         const PetscScalar *flip  = flips ? flips[p] : NULL;
7278         PetscCall(PetscSectionGetDof(section, point, &dof));
7279         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
7280       }
7281       break;
7282     case INSERT_ALL_VALUES:
7283       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7284         const PetscInt     point = points[2 * p];
7285         const PetscInt    *perm  = perms ? perms[p] : NULL;
7286         const PetscScalar *flip  = flips ? flips[p] : NULL;
7287         PetscCall(PetscSectionGetDof(section, point, &dof));
7288         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
7289       }
7290       break;
7291     case INSERT_BC_VALUES:
7292       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7293         const PetscInt     point = points[2 * p];
7294         const PetscInt    *perm  = perms ? perms[p] : NULL;
7295         const PetscScalar *flip  = flips ? flips[p] : NULL;
7296         PetscCall(PetscSectionGetDof(section, point, &dof));
7297         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
7298       }
7299       break;
7300     case ADD_VALUES:
7301       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7302         const PetscInt     point = points[2 * p];
7303         const PetscInt    *perm  = perms ? perms[p] : NULL;
7304         const PetscScalar *flip  = flips ? flips[p] : NULL;
7305         PetscCall(PetscSectionGetDof(section, point, &dof));
7306         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
7307       }
7308       break;
7309     case ADD_ALL_VALUES:
7310       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7311         const PetscInt     point = points[2 * p];
7312         const PetscInt    *perm  = perms ? perms[p] : NULL;
7313         const PetscScalar *flip  = flips ? flips[p] : NULL;
7314         PetscCall(PetscSectionGetDof(section, point, &dof));
7315         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
7316       }
7317       break;
7318     case ADD_BC_VALUES:
7319       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7320         const PetscInt     point = points[2 * p];
7321         const PetscInt    *perm  = perms ? perms[p] : NULL;
7322         const PetscScalar *flip  = flips ? flips[p] : NULL;
7323         PetscCall(PetscSectionGetDof(section, point, &dof));
7324         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
7325       }
7326       break;
7327     default:
7328       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7329     }
7330     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
7331   }
7332   /* Cleanup points */
7333   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7334   /* Cleanup array */
7335   PetscCall(VecRestoreArray(v, &array));
7336   PetscFunctionReturn(PETSC_SUCCESS);
7337 }
7338 
7339 /* Check whether the given point is in the label. If not, update the offset to skip this point */
7340 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
7341 {
7342   PetscFunctionBegin;
7343   *contains = PETSC_TRUE;
7344   if (label) {
7345     PetscInt fdof;
7346 
7347     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
7348     if (!*contains) {
7349       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7350       *offset += fdof;
7351       PetscFunctionReturn(PETSC_SUCCESS);
7352     }
7353   }
7354   PetscFunctionReturn(PETSC_SUCCESS);
7355 }
7356 
7357 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
7358 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)
7359 {
7360   PetscSection    clSection;
7361   IS              clPoints;
7362   PetscScalar    *array;
7363   PetscInt       *points = NULL;
7364   const PetscInt *clp;
7365   PetscInt        numFields, numPoints, p;
7366   PetscInt        offset = 0, f;
7367 
7368   PetscFunctionBeginHot;
7369   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7370   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7371   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7372   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7373   PetscCall(PetscSectionGetNumFields(section, &numFields));
7374   /* Get points */
7375   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7376   /* Get array */
7377   PetscCall(VecGetArray(v, &array));
7378   /* Get values */
7379   for (f = 0; f < numFields; ++f) {
7380     const PetscInt    **perms = NULL;
7381     const PetscScalar **flips = NULL;
7382     PetscBool           contains;
7383 
7384     if (!fieldActive[f]) {
7385       for (p = 0; p < numPoints * 2; p += 2) {
7386         PetscInt fdof;
7387         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7388         offset += fdof;
7389       }
7390       continue;
7391     }
7392     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7393     switch (mode) {
7394     case INSERT_VALUES:
7395       for (p = 0; p < numPoints; p++) {
7396         const PetscInt     point = points[2 * p];
7397         const PetscInt    *perm  = perms ? perms[p] : NULL;
7398         const PetscScalar *flip  = flips ? flips[p] : NULL;
7399         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7400         if (!contains) continue;
7401         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
7402       }
7403       break;
7404     case INSERT_ALL_VALUES:
7405       for (p = 0; p < numPoints; p++) {
7406         const PetscInt     point = points[2 * p];
7407         const PetscInt    *perm  = perms ? perms[p] : NULL;
7408         const PetscScalar *flip  = flips ? flips[p] : NULL;
7409         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7410         if (!contains) continue;
7411         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
7412       }
7413       break;
7414     case INSERT_BC_VALUES:
7415       for (p = 0; p < numPoints; p++) {
7416         const PetscInt     point = points[2 * p];
7417         const PetscInt    *perm  = perms ? perms[p] : NULL;
7418         const PetscScalar *flip  = flips ? flips[p] : NULL;
7419         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7420         if (!contains) continue;
7421         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
7422       }
7423       break;
7424     case ADD_VALUES:
7425       for (p = 0; p < numPoints; p++) {
7426         const PetscInt     point = points[2 * p];
7427         const PetscInt    *perm  = perms ? perms[p] : NULL;
7428         const PetscScalar *flip  = flips ? flips[p] : NULL;
7429         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7430         if (!contains) continue;
7431         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
7432       }
7433       break;
7434     case ADD_ALL_VALUES:
7435       for (p = 0; p < numPoints; p++) {
7436         const PetscInt     point = points[2 * p];
7437         const PetscInt    *perm  = perms ? perms[p] : NULL;
7438         const PetscScalar *flip  = flips ? flips[p] : NULL;
7439         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7440         if (!contains) continue;
7441         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
7442       }
7443       break;
7444     default:
7445       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7446     }
7447     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7448   }
7449   /* Cleanup points */
7450   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7451   /* Cleanup array */
7452   PetscCall(VecRestoreArray(v, &array));
7453   PetscFunctionReturn(PETSC_SUCCESS);
7454 }
7455 
7456 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
7457 {
7458   PetscMPIInt rank;
7459   PetscInt    i, j;
7460 
7461   PetscFunctionBegin;
7462   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7463   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
7464   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
7465   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
7466   numCIndices = numCIndices ? numCIndices : numRIndices;
7467   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
7468   for (i = 0; i < numRIndices; i++) {
7469     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
7470     for (j = 0; j < numCIndices; j++) {
7471 #if defined(PETSC_USE_COMPLEX)
7472       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
7473 #else
7474       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
7475 #endif
7476     }
7477     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
7478   }
7479   PetscFunctionReturn(PETSC_SUCCESS);
7480 }
7481 
7482 /*
7483   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
7484 
7485   Input Parameters:
7486 + section - The section for this data layout
7487 . islocal - Is the section (and thus indices being requested) local or global?
7488 . point   - The point contributing dofs with these indices
7489 . off     - The global offset of this point
7490 . loff    - The local offset of each field
7491 . setBC   - The flag determining whether to include indices of boundary values
7492 . perm    - A permutation of the dofs on this point, or NULL
7493 - indperm - A permutation of the entire indices array, or NULL
7494 
7495   Output Parameter:
7496 . indices - Indices for dofs on this point
7497 
7498   Level: developer
7499 
7500   Note: The indices could be local or global, depending on the value of 'off'.
7501 */
7502 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7503 {
7504   PetscInt        dof;   /* The number of unknowns on this point */
7505   PetscInt        cdof;  /* The number of constraints on this point */
7506   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7507   PetscInt        cind = 0, k;
7508 
7509   PetscFunctionBegin;
7510   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7511   PetscCall(PetscSectionGetDof(section, point, &dof));
7512   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7513   if (!cdof || setBC) {
7514     for (k = 0; k < dof; ++k) {
7515       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7516       const PetscInt ind    = indperm ? indperm[preind] : preind;
7517 
7518       indices[ind] = off + k;
7519     }
7520   } else {
7521     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7522     for (k = 0; k < dof; ++k) {
7523       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7524       const PetscInt ind    = indperm ? indperm[preind] : preind;
7525 
7526       if ((cind < cdof) && (k == cdofs[cind])) {
7527         /* Insert check for returning constrained indices */
7528         indices[ind] = -(off + k + 1);
7529         ++cind;
7530       } else {
7531         indices[ind] = off + k - (islocal ? 0 : cind);
7532       }
7533     }
7534   }
7535   *loff += dof;
7536   PetscFunctionReturn(PETSC_SUCCESS);
7537 }
7538 
7539 /*
7540  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7541 
7542  Input Parameters:
7543 + section - a section (global or local)
7544 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7545 . point - point within section
7546 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7547 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7548 . setBC - identify constrained (boundary condition) points via involution.
7549 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7550 . permsoff - offset
7551 - indperm - index permutation
7552 
7553  Output Parameter:
7554 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7555 . indices - array to hold indices (as defined by section) of each dof associated with point
7556 
7557  Notes:
7558  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7559  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7560  in the local vector.
7561 
7562  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7563  significant).  It is invalid to call with a global section and setBC=true.
7564 
7565  Developer Note:
7566  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7567  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7568  offset could be obtained from the section instead of passing it explicitly as we do now.
7569 
7570  Example:
7571  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7572  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7573  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7574  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.
7575 
7576  Level: developer
7577 */
7578 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[])
7579 {
7580   PetscInt numFields, foff, f;
7581 
7582   PetscFunctionBegin;
7583   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7584   PetscCall(PetscSectionGetNumFields(section, &numFields));
7585   for (f = 0, foff = 0; f < numFields; ++f) {
7586     PetscInt        fdof, cfdof;
7587     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7588     PetscInt        cind = 0, b;
7589     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7590 
7591     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7592     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7593     if (!cfdof || setBC) {
7594       for (b = 0; b < fdof; ++b) {
7595         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7596         const PetscInt ind    = indperm ? indperm[preind] : preind;
7597 
7598         indices[ind] = off + foff + b;
7599       }
7600     } else {
7601       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7602       for (b = 0; b < fdof; ++b) {
7603         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7604         const PetscInt ind    = indperm ? indperm[preind] : preind;
7605 
7606         if ((cind < cfdof) && (b == fcdofs[cind])) {
7607           indices[ind] = -(off + foff + b + 1);
7608           ++cind;
7609         } else {
7610           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7611         }
7612       }
7613     }
7614     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7615     foffs[f] += fdof;
7616   }
7617   PetscFunctionReturn(PETSC_SUCCESS);
7618 }
7619 
7620 /*
7621   This version believes the globalSection offsets for each field, rather than just the point offset
7622 
7623  . foffs - The offset into 'indices' for each field, since it is segregated by field
7624 
7625  Notes:
7626  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7627  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7628 */
7629 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7630 {
7631   PetscInt numFields, foff, f;
7632 
7633   PetscFunctionBegin;
7634   PetscCall(PetscSectionGetNumFields(section, &numFields));
7635   for (f = 0; f < numFields; ++f) {
7636     PetscInt        fdof, cfdof;
7637     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7638     PetscInt        cind = 0, b;
7639     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7640 
7641     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7642     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7643     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7644     if (!cfdof) {
7645       for (b = 0; b < fdof; ++b) {
7646         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7647         const PetscInt ind    = indperm ? indperm[preind] : preind;
7648 
7649         indices[ind] = foff + b;
7650       }
7651     } else {
7652       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7653       for (b = 0; b < fdof; ++b) {
7654         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7655         const PetscInt ind    = indperm ? indperm[preind] : preind;
7656 
7657         if ((cind < cfdof) && (b == fcdofs[cind])) {
7658           indices[ind] = -(foff + b + 1);
7659           ++cind;
7660         } else {
7661           indices[ind] = foff + b - cind;
7662         }
7663       }
7664     }
7665     foffs[f] += fdof;
7666   }
7667   PetscFunctionReturn(PETSC_SUCCESS);
7668 }
7669 
7670 static PetscErrorCode DMPlexAnchorsGetSubMatIndices(PetscInt nPoints, const PetscInt pnts[], PetscSection section, PetscSection cSec, PetscInt tmpIndices[], PetscInt fieldOffsets[], PetscInt indices[], const PetscInt ***perms)
7671 {
7672   PetscInt numFields, sStart, sEnd, cStart, cEnd;
7673 
7674   PetscFunctionBegin;
7675   PetscCall(PetscSectionGetNumFields(section, &numFields));
7676   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7677   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7678   for (PetscInt p = 0; p < nPoints; p++) {
7679     PetscInt     b       = pnts[2 * p];
7680     PetscInt     bSecDof = 0, bOff;
7681     PetscInt     cSecDof = 0;
7682     PetscSection indices_section;
7683 
7684     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7685     if (!bSecDof) continue;
7686     if (b >= cStart && b < cEnd) PetscCall(PetscSectionGetDof(cSec, b, &cSecDof));
7687     indices_section = cSecDof > 0 ? cSec : section;
7688     if (numFields) {
7689       PetscInt fStart[32], fEnd[32];
7690 
7691       fStart[0] = 0;
7692       fEnd[0]   = 0;
7693       for (PetscInt f = 0; f < numFields; f++) {
7694         PetscInt fDof = 0;
7695 
7696         PetscCall(PetscSectionGetFieldDof(indices_section, b, f, &fDof));
7697         fStart[f + 1] = fStart[f] + fDof;
7698         fEnd[f + 1]   = fStart[f + 1];
7699       }
7700       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7701       // only apply permutations on one side
7702       PetscCall(DMPlexGetIndicesPointFields_Internal(indices_section, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, perms ? p : -1, NULL, tmpIndices));
7703       for (PetscInt f = 0; f < numFields; f++) {
7704         for (PetscInt i = fStart[f]; i < fEnd[f]; i++) { indices[fieldOffsets[f]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1); }
7705       }
7706     } else {
7707       PetscInt bEnd = 0;
7708 
7709       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7710       PetscCall(DMPlexGetIndicesPoint_Internal(indices_section, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, tmpIndices));
7711 
7712       for (PetscInt i = 0; i < bEnd; i++) indices[fieldOffsets[0]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1);
7713     }
7714   }
7715   PetscFunctionReturn(PETSC_SUCCESS);
7716 }
7717 
7718 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[])
7719 {
7720   Mat             cMat;
7721   PetscSection    aSec, cSec;
7722   IS              aIS;
7723   PetscInt        aStart = -1, aEnd = -1;
7724   PetscInt        sStart = -1, sEnd = -1;
7725   PetscInt        cStart = -1, cEnd = -1;
7726   const PetscInt *anchors;
7727   PetscInt        numFields, p;
7728   PetscInt        newNumPoints = 0, newNumIndices = 0;
7729   PetscInt       *newPoints, *indices, *newIndices, *tmpIndices, *tmpNewIndices;
7730   PetscInt        oldOffsets[32];
7731   PetscInt        newOffsets[32];
7732   PetscInt        oldOffsetsCopy[32];
7733   PetscInt        newOffsetsCopy[32];
7734   PetscScalar    *modMat         = NULL;
7735   PetscBool       anyConstrained = PETSC_FALSE;
7736 
7737   PetscFunctionBegin;
7738   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7739   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7740   PetscCall(PetscSectionGetNumFields(section, &numFields));
7741 
7742   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7743   /* if there are point-to-point constraints */
7744   if (aSec) {
7745     PetscCall(PetscArrayzero(newOffsets, 32));
7746     PetscCall(PetscArrayzero(oldOffsets, 32));
7747     PetscCall(ISGetIndices(aIS, &anchors));
7748     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7749     PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7750     /* figure out how many points are going to be in the new element matrix
7751      * (we allow double counting, because it's all just going to be summed
7752      * into the global matrix anyway) */
7753     for (p = 0; p < 2 * numPoints; p += 2) {
7754       PetscInt b    = points[p];
7755       PetscInt bDof = 0, bSecDof = 0;
7756 
7757       if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7758       if (!bSecDof) continue;
7759 
7760       for (PetscInt f = 0; f < numFields; f++) {
7761         PetscInt fDof = 0;
7762 
7763         PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7764         oldOffsets[f + 1] += fDof;
7765       }
7766       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7767       if (bDof) {
7768         /* this point is constrained */
7769         /* it is going to be replaced by its anchors */
7770         PetscInt bOff, q;
7771 
7772         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7773         for (q = 0; q < bDof; q++) {
7774           PetscInt a    = anchors[bOff + q];
7775           PetscInt aDof = 0;
7776 
7777           if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7778           if (aDof) {
7779             anyConstrained = PETSC_TRUE;
7780             newNumPoints += 1;
7781           }
7782           newNumIndices += aDof;
7783           for (PetscInt f = 0; f < numFields; ++f) {
7784             PetscInt fDof = 0;
7785 
7786             if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7787             newOffsets[f + 1] += fDof;
7788           }
7789         }
7790       } else {
7791         /* this point is not constrained */
7792         newNumPoints++;
7793         newNumIndices += bSecDof;
7794         for (PetscInt f = 0; f < numFields; ++f) {
7795           PetscInt fDof;
7796 
7797           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7798           newOffsets[f + 1] += fDof;
7799         }
7800       }
7801     }
7802   }
7803   if (!anyConstrained) {
7804     if (outNumPoints) *outNumPoints = 0;
7805     if (outNumIndices) *outNumIndices = 0;
7806     if (outPoints) *outPoints = NULL;
7807     if (outMat) *outMat = NULL;
7808     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7809     PetscFunctionReturn(PETSC_SUCCESS);
7810   }
7811 
7812   if (outNumPoints) *outNumPoints = newNumPoints;
7813   if (outNumIndices) *outNumIndices = newNumIndices;
7814 
7815   for (PetscInt f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7816   for (PetscInt f = 0; f < numFields; ++f) oldOffsets[f + 1] += oldOffsets[f];
7817 
7818   if (!outPoints && !outMat) {
7819     if (offsets) {
7820       for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7821     }
7822     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7823     PetscFunctionReturn(PETSC_SUCCESS);
7824   }
7825 
7826   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7827   PetscCheck(!numFields || oldOffsets[numFields] == numIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, oldOffsets[numFields], numIndices);
7828 
7829   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7830   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7831 
7832   /* output arrays */
7833   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7834   PetscCall(PetscArrayzero(newPoints, 2 * newNumPoints));
7835 
7836   // get the new Points
7837   for (PetscInt p = 0, newP = 0; p < numPoints; p++) {
7838     PetscInt b    = points[2 * p];
7839     PetscInt bDof = 0, bSecDof = 0, bOff;
7840 
7841     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7842     if (!bSecDof) continue;
7843     if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7844     if (bDof) {
7845       PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7846       for (PetscInt q = 0; q < bDof; q++) {
7847         PetscInt a = anchors[bOff + q], aDof = 0;
7848 
7849         if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7850         if (aDof) {
7851           newPoints[2 * newP]     = a;
7852           newPoints[2 * newP + 1] = 0; // orientations are accounted for in constructing the matrix, newly added points are in default orientation
7853           newP++;
7854         }
7855       }
7856     } else {
7857       newPoints[2 * newP]     = b;
7858       newPoints[2 * newP + 1] = points[2 * p + 1];
7859       newP++;
7860     }
7861   }
7862 
7863   if (outMat) {
7864     PetscScalar *tmpMat;
7865     PetscCall(PetscArraycpy(oldOffsetsCopy, oldOffsets, 32));
7866     PetscCall(PetscArraycpy(newOffsetsCopy, newOffsets, 32));
7867 
7868     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &indices));
7869     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7870     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7871     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7872 
7873     for (PetscInt i = 0; i < numIndices; i++) indices[i] = -1;
7874     for (PetscInt i = 0; i < newNumIndices; i++) newIndices[i] = -1;
7875 
7876     PetscCall(DMPlexAnchorsGetSubMatIndices(numPoints, points, section, cSec, tmpIndices, oldOffsetsCopy, indices, perms));
7877     PetscCall(DMPlexAnchorsGetSubMatIndices(newNumPoints, newPoints, section, section, tmpNewIndices, newOffsetsCopy, newIndices, NULL));
7878 
7879     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7880     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7881     PetscCall(PetscArrayzero(modMat, newNumIndices * numIndices));
7882     // for each field, insert the anchor modification into modMat
7883     for (PetscInt f = 0; f < PetscMax(1, numFields); f++) {
7884       PetscInt fStart    = oldOffsets[f];
7885       PetscInt fNewStart = newOffsets[f];
7886       for (PetscInt p = 0, newP = 0, o = fStart, oNew = fNewStart; p < numPoints; p++) {
7887         PetscInt b    = points[2 * p];
7888         PetscInt bDof = 0, bSecDof = 0, bOff;
7889 
7890         if (b >= sStart && b < sEnd) {
7891           if (numFields) {
7892             PetscCall(PetscSectionGetFieldDof(section, b, f, &bSecDof));
7893           } else {
7894             PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7895           }
7896         }
7897         if (!bSecDof) continue;
7898         if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7899         if (bDof) {
7900           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7901           for (PetscInt q = 0; q < bDof; q++, newP++) {
7902             PetscInt a = anchors[bOff + q], aDof = 0;
7903 
7904             if (a >= sStart && a < sEnd) {
7905               if (numFields) {
7906                 PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
7907               } else {
7908                 PetscCall(PetscSectionGetDof(section, a, &aDof));
7909               }
7910             }
7911             if (aDof) {
7912               PetscCall(MatGetValues(cMat, bSecDof, &indices[o], aDof, &newIndices[oNew], tmpMat));
7913               for (PetscInt d = 0; d < bSecDof; d++) {
7914                 for (PetscInt e = 0; e < aDof; e++) modMat[(o + d) * newNumIndices + oNew + e] = tmpMat[d * aDof + e];
7915               }
7916             }
7917             oNew += aDof;
7918           }
7919         } else {
7920           // Insert the identity matrix in this block
7921           for (PetscInt d = 0; d < bSecDof; d++) modMat[(o + d) * newNumIndices + oNew + d] = 1;
7922           oNew += bSecDof;
7923           newP++;
7924         }
7925         o += bSecDof;
7926       }
7927     }
7928 
7929     *outMat = modMat;
7930 
7931     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7932     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7933     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7934     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7935     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &indices));
7936   }
7937   PetscCall(ISRestoreIndices(aIS, &anchors));
7938 
7939   /* output */
7940   if (outPoints) {
7941     *outPoints = newPoints;
7942   } else {
7943     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7944   }
7945   for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7946   PetscFunctionReturn(PETSC_SUCCESS);
7947 }
7948 
7949 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)
7950 {
7951   PetscScalar *modMat        = NULL;
7952   PetscInt     newNumIndices = -1;
7953 
7954   PetscFunctionBegin;
7955   /* 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.
7956      modMat is that matrix C */
7957   PetscCall(DMPlexAnchorsGetSubMatModification(dm, section, numPoints, numIndices, points, perms, outNumPoints, &newNumIndices, outPoints, offsets, outValues ? &modMat : NULL));
7958   if (outNumIndices) *outNumIndices = newNumIndices;
7959   if (modMat) {
7960     const PetscScalar *newValues = values;
7961 
7962     if (multiplyRight) {
7963       PetscScalar *newNewValues = NULL;
7964       PetscBLASInt M, N, K;
7965       PetscScalar  a = 1.0, b = 0.0;
7966 
7967       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);
7968 
7969       PetscCall(PetscBLASIntCast(newNumIndices, &M));
7970       PetscCall(PetscBLASIntCast(numRows, &N));
7971       PetscCall(PetscBLASIntCast(numIndices, &K));
7972       PetscCall(DMGetWorkArray(dm, numRows * newNumIndices, MPIU_SCALAR, &newNewValues));
7973       // row-major to column-major conversion, right multiplication becomes left multiplication
7974       PetscCallBLAS("BLASgemm", BLASgemm_("N", "N", &M, &N, &K, &a, modMat, &M, newValues, &K, &b, newNewValues, &M));
7975       numCols   = newNumIndices;
7976       newValues = newNewValues;
7977     }
7978 
7979     if (multiplyLeft) {
7980       PetscScalar *newNewValues = NULL;
7981       PetscBLASInt M, N, K;
7982       PetscScalar  a = 1.0, b = 0.0;
7983 
7984       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);
7985 
7986       PetscCall(PetscBLASIntCast(numCols, &M));
7987       PetscCall(PetscBLASIntCast(newNumIndices, &N));
7988       PetscCall(PetscBLASIntCast(numIndices, &K));
7989       PetscCall(DMGetWorkArray(dm, newNumIndices * numCols, MPIU_SCALAR, &newNewValues));
7990       // row-major to column-major conversion, left multiplication becomes right multiplication
7991       PetscCallBLAS("BLASgemm", BLASgemm_("N", "T", &M, &N, &K, &a, newValues, &M, modMat, &N, &b, newNewValues, &M));
7992       if (newValues != values) PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &newValues));
7993       newValues = newNewValues;
7994     }
7995     *outValues = (PetscScalar *)newValues;
7996     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7997   }
7998   PetscFunctionReturn(PETSC_SUCCESS);
7999 }
8000 
8001 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)
8002 {
8003   PetscFunctionBegin;
8004   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, numPoints, numIndices, points, perms, numIndices, numIndices, values, outNumPoints, outNumIndices, outPoints, outValues, offsets, PETSC_TRUE, multiplyLeft));
8005   PetscFunctionReturn(PETSC_SUCCESS);
8006 }
8007 
8008 static PetscErrorCode DMPlexGetClosureIndicesSize_Internal(DM dm, PetscSection section, PetscInt point, PetscInt *closureSize)
8009 {
8010   /* Closure ordering */
8011   PetscSection    clSection;
8012   IS              clPoints;
8013   const PetscInt *clp;
8014   PetscInt       *points;
8015   PetscInt        Ncl, Ni = 0;
8016 
8017   PetscFunctionBeginHot;
8018   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
8019   for (PetscInt p = 0; p < Ncl * 2; p += 2) {
8020     PetscInt dof;
8021 
8022     PetscCall(PetscSectionGetDof(section, points[p], &dof));
8023     Ni += dof;
8024   }
8025   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8026   *closureSize = Ni;
8027   PetscFunctionReturn(PETSC_SUCCESS);
8028 }
8029 
8030 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)
8031 {
8032   /* Closure ordering */
8033   PetscSection    clSection;
8034   IS              clPoints;
8035   const PetscInt *clp;
8036   PetscInt       *points;
8037   const PetscInt *clperm = NULL;
8038   /* Dof permutation and sign flips */
8039   const PetscInt    **perms[32] = {NULL};
8040   const PetscScalar **flips[32] = {NULL};
8041   PetscScalar        *valCopy   = NULL;
8042   /* Hanging node constraints */
8043   PetscInt    *pointsC = NULL;
8044   PetscScalar *valuesC = NULL;
8045   PetscInt     NclC, NiC;
8046 
8047   PetscInt *idx;
8048   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
8049   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
8050   PetscInt  idxStart, idxEnd;
8051   PetscInt  nRows, nCols;
8052 
8053   PetscFunctionBeginHot;
8054   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8055   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8056   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
8057   PetscAssertPointer(numRows, 6);
8058   PetscAssertPointer(numCols, 7);
8059   if (indices) PetscAssertPointer(indices, 8);
8060   if (outOffsets) PetscAssertPointer(outOffsets, 9);
8061   if (values) PetscAssertPointer(values, 10);
8062   PetscCall(PetscSectionGetNumFields(section, &Nf));
8063   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
8064   PetscCall(PetscArrayzero(offsets, 32));
8065   /* 1) Get points in closure */
8066   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
8067   if (useClPerm) {
8068     PetscInt depth, clsize;
8069     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
8070     for (clsize = 0, p = 0; p < Ncl; p++) {
8071       PetscInt dof;
8072       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
8073       clsize += dof;
8074     }
8075     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
8076   }
8077   /* 2) Get number of indices on these points and field offsets from section */
8078   for (p = 0; p < Ncl * 2; p += 2) {
8079     PetscInt dof, fdof;
8080 
8081     PetscCall(PetscSectionGetDof(section, points[p], &dof));
8082     for (f = 0; f < Nf; ++f) {
8083       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
8084       offsets[f + 1] += fdof;
8085     }
8086     Ni += dof;
8087   }
8088   if (*numRows == -1) *numRows = Ni;
8089   if (*numCols == -1) *numCols = Ni;
8090   nRows = *numRows;
8091   nCols = *numCols;
8092   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
8093   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
8094   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
8095   if (multiplyRight) PetscCheck(nCols == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " columns, got %" PetscInt_FMT, Ni, nCols);
8096   if (multiplyLeft) PetscCheck(nRows == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " rows, got %" PetscInt_FMT, Ni, nRows);
8097   for (f = 0; f < PetscMax(1, Nf); ++f) {
8098     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8099     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
8100     /* may need to apply sign changes to the element matrix */
8101     if (values && flips[f]) {
8102       PetscInt foffset = offsets[f];
8103 
8104       for (p = 0; p < Ncl; ++p) {
8105         PetscInt           pnt  = points[2 * p], fdof;
8106         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
8107 
8108         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
8109         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
8110         if (flip) {
8111           PetscInt i, j, k;
8112 
8113           if (!valCopy) {
8114             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8115             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
8116             *values = valCopy;
8117           }
8118           for (i = 0; i < fdof; ++i) {
8119             PetscScalar fval = flip[i];
8120 
8121             if (multiplyRight) {
8122               for (k = 0; k < nRows; ++k) { valCopy[Ni * k + (foffset + i)] *= fval; }
8123             }
8124             if (multiplyLeft) {
8125               for (k = 0; k < nCols; ++k) { valCopy[nCols * (foffset + i) + k] *= fval; }
8126             }
8127           }
8128         }
8129         foffset += fdof;
8130       }
8131     }
8132   }
8133   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
8134   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, Ncl, Ni, points, perms, nRows, nCols, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, multiplyRight, multiplyLeft));
8135   if (NclC) {
8136     if (multiplyRight) *numCols = NiC;
8137     if (multiplyLeft) *numRows = NiC;
8138     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8139     for (f = 0; f < PetscMax(1, Nf); ++f) {
8140       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8141       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8142     }
8143     for (f = 0; f < PetscMax(1, Nf); ++f) {
8144       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
8145       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
8146     }
8147     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8148     Ncl    = NclC;
8149     Ni     = NiC;
8150     points = pointsC;
8151     if (values) *values = valuesC;
8152   }
8153   /* 5) Calculate indices */
8154   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
8155   PetscCall(PetscSectionGetChart(idxSection, &idxStart, &idxEnd));
8156   if (Nf) {
8157     PetscInt  idxOff;
8158     PetscBool useFieldOffsets;
8159 
8160     if (outOffsets) {
8161       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
8162     }
8163     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
8164     if (useFieldOffsets) {
8165       for (p = 0; p < Ncl; ++p) {
8166         const PetscInt pnt = points[p * 2];
8167 
8168         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
8169       }
8170     } else {
8171       for (p = 0; p < Ncl; ++p) {
8172         const PetscInt pnt = points[p * 2];
8173 
8174         if (pnt < idxStart || pnt >= idxEnd) continue;
8175         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8176         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8177          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
8178          * global section. */
8179         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
8180       }
8181     }
8182   } else {
8183     PetscInt off = 0, idxOff;
8184 
8185     for (p = 0; p < Ncl; ++p) {
8186       const PetscInt  pnt  = points[p * 2];
8187       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
8188 
8189       if (pnt < idxStart || pnt >= idxEnd) continue;
8190       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8191       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8192        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
8193       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
8194     }
8195   }
8196   /* 6) Cleanup */
8197   for (f = 0; f < PetscMax(1, Nf); ++f) {
8198     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8199     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8200   }
8201   if (NclC) {
8202     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
8203   } else {
8204     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8205   }
8206 
8207   if (indices) *indices = idx;
8208   PetscFunctionReturn(PETSC_SUCCESS);
8209 }
8210 
8211 /*@C
8212   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
8213 
8214   Not collective
8215 
8216   Input Parameters:
8217 + dm         - The `DM`
8218 . section    - The `PetscSection` describing the points (a local section)
8219 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8220 . point      - The point defining the closure
8221 - useClPerm  - Use the closure point permutation if available
8222 
8223   Output Parameters:
8224 + numIndices - The number of dof indices in the closure of point with the input sections
8225 . indices    - The dof indices
8226 . outOffsets - Array to write the field offsets into, or `NULL`
8227 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8228 
8229   Level: advanced
8230 
8231   Notes:
8232   Call `DMPlexRestoreClosureIndices()` to free allocated memory
8233 
8234   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8235   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
8236   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8237   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
8238   indices (with the above semantics) are implied.
8239 
8240 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
8241           `PetscSection`, `DMGetGlobalSection()`
8242 @*/
8243 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PeOp PetscInt outOffsets[], PeOp PetscScalar *values[])
8244 {
8245   PetscInt numRows = -1, numCols = -1;
8246 
8247   PetscFunctionBeginHot;
8248   PetscCall(DMPlexGetClosureIndices_Internal(dm, section, idxSection, point, useClPerm, &numRows, &numCols, indices, outOffsets, values, PETSC_TRUE, PETSC_TRUE));
8249   PetscCheck(numRows == numCols, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Symmetric matrix transformation produces rectangular dimensions (%" PetscInt_FMT ", %" PetscInt_FMT ")", numRows, numCols);
8250   *numIndices = numRows;
8251   PetscFunctionReturn(PETSC_SUCCESS);
8252 }
8253 
8254 /*@C
8255   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
8256 
8257   Not collective
8258 
8259   Input Parameters:
8260 + dm         - The `DM`
8261 . section    - The `PetscSection` describing the points (a local section)
8262 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8263 . point      - The point defining the closure
8264 - useClPerm  - Use the closure point permutation if available
8265 
8266   Output Parameters:
8267 + numIndices - The number of dof indices in the closure of point with the input sections
8268 . indices    - The dof indices
8269 . outOffsets - Array to write the field offsets into, or `NULL`
8270 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8271 
8272   Level: advanced
8273 
8274   Notes:
8275   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
8276 
8277   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8278   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
8279   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8280   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
8281   indices (with the above semantics) are implied.
8282 
8283 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
8284 @*/
8285 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PeOp PetscInt outOffsets[], PeOp PetscScalar *values[])
8286 {
8287   PetscFunctionBegin;
8288   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8289   PetscAssertPointer(indices, 7);
8290   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
8291   PetscFunctionReturn(PETSC_SUCCESS);
8292 }
8293 
8294 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8295 {
8296   DM_Plex           *mesh = (DM_Plex *)dm->data;
8297   PetscInt          *indices;
8298   PetscInt           numIndices;
8299   const PetscScalar *valuesOrig = values;
8300   PetscErrorCode     ierr;
8301 
8302   PetscFunctionBegin;
8303   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8304   if (!section) PetscCall(DMGetLocalSection(dm, &section));
8305   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8306   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
8307   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
8308   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
8309 
8310   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
8311 
8312   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
8313   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8314   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
8315   if (ierr) {
8316     PetscMPIInt rank;
8317 
8318     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8319     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8320     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
8321     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8322     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8323     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
8324   }
8325   if (mesh->printFEM > 1) {
8326     PetscInt i;
8327     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
8328     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
8329     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8330   }
8331 
8332   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8333   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8334   PetscFunctionReturn(PETSC_SUCCESS);
8335 }
8336 
8337 /*@C
8338   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
8339 
8340   Not collective
8341 
8342   Input Parameters:
8343 + dm            - The `DM`
8344 . section       - The section describing the layout in `v`, or `NULL` to use the default section
8345 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
8346 . A             - The matrix
8347 . point         - The point in the `DM`
8348 . values        - The array of values
8349 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8350 
8351   Level: intermediate
8352 
8353 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8354 @*/
8355 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8356 {
8357   PetscFunctionBegin;
8358   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
8359   PetscFunctionReturn(PETSC_SUCCESS);
8360 }
8361 
8362 /*@C
8363   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
8364 
8365   Not collective
8366 
8367   Input Parameters:
8368 + dmRow            - The `DM` for the row fields
8369 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
8370 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
8371 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
8372 . dmCol            - The `DM` for the column fields
8373 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
8374 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
8375 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
8376 . A                - The matrix
8377 . point            - The point in the `DM`
8378 . values           - The array of values
8379 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8380 
8381   Level: intermediate
8382 
8383 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8384 @*/
8385 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)
8386 {
8387   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
8388   PetscInt          *indicesRow, *indicesCol;
8389   PetscInt           numIndicesRow = -1, numIndicesCol = -1;
8390   const PetscScalar *valuesV0 = values, *valuesV1, *valuesV2;
8391 
8392   PetscErrorCode ierr;
8393 
8394   PetscFunctionBegin;
8395   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
8396   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
8397   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
8398   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
8399   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
8400   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
8401   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
8402   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
8403   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
8404   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
8405   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
8406 
8407   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmRow, sectionRow, point, &numIndicesRow));
8408   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmCol, sectionCol, point, &numIndicesCol));
8409   valuesV1 = valuesV0;
8410   PetscCall(DMPlexGetClosureIndices_Internal(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV1, PETSC_FALSE, PETSC_TRUE));
8411   valuesV2 = valuesV1;
8412   PetscCall(DMPlexGetClosureIndices_Internal(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesRow, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2, PETSC_TRUE, PETSC_FALSE));
8413 
8414   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2));
8415   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8416   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2, mode);
8417   if (ierr) {
8418     PetscMPIInt rank;
8419 
8420     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8421     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8422     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8423     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV2));
8424     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8425     if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8426     if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8427   }
8428 
8429   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2));
8430   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8431   if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8432   if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8433   PetscFunctionReturn(PETSC_SUCCESS);
8434 }
8435 
8436 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8437 {
8438   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8439   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8440   PetscInt       *cpoints = NULL;
8441   PetscInt       *findices, *cindices;
8442   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8443   PetscInt        foffsets[32], coffsets[32];
8444   DMPolytopeType  ct;
8445   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8446   PetscErrorCode  ierr;
8447 
8448   PetscFunctionBegin;
8449   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8450   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8451   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8452   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8453   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8454   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8455   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8456   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8457   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8458   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8459   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8460   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8461   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8462   PetscCall(PetscArrayzero(foffsets, 32));
8463   PetscCall(PetscArrayzero(coffsets, 32));
8464   /* Column indices */
8465   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8466   maxFPoints = numCPoints;
8467   /* Compress out points not in the section */
8468   /*   TODO: Squeeze out points with 0 dof as well */
8469   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8470   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8471     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8472       cpoints[q * 2]     = cpoints[p];
8473       cpoints[q * 2 + 1] = cpoints[p + 1];
8474       ++q;
8475     }
8476   }
8477   numCPoints = q;
8478   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8479     PetscInt fdof;
8480 
8481     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8482     if (!dof) continue;
8483     for (f = 0; f < numFields; ++f) {
8484       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8485       coffsets[f + 1] += fdof;
8486     }
8487     numCIndices += dof;
8488   }
8489   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8490   /* Row indices */
8491   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8492   {
8493     DMPlexTransform tr;
8494     DMPolytopeType *rct;
8495     PetscInt       *rsize, *rcone, *rornt, Nt;
8496 
8497     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8498     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8499     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8500     numSubcells = rsize[Nt - 1];
8501     PetscCall(DMPlexTransformDestroy(&tr));
8502   }
8503   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8504   for (r = 0, q = 0; r < numSubcells; ++r) {
8505     /* TODO Map from coarse to fine cells */
8506     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8507     /* Compress out points not in the section */
8508     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8509     for (p = 0; p < numFPoints * 2; p += 2) {
8510       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8511         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8512         if (!dof) continue;
8513         for (s = 0; s < q; ++s)
8514           if (fpoints[p] == ftotpoints[s * 2]) break;
8515         if (s < q) continue;
8516         ftotpoints[q * 2]     = fpoints[p];
8517         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8518         ++q;
8519       }
8520     }
8521     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8522   }
8523   numFPoints = q;
8524   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8525     PetscInt fdof;
8526 
8527     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8528     if (!dof) continue;
8529     for (f = 0; f < numFields; ++f) {
8530       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8531       foffsets[f + 1] += fdof;
8532     }
8533     numFIndices += dof;
8534   }
8535   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8536 
8537   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8538   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8539   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8540   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8541   if (numFields) {
8542     const PetscInt **permsF[32] = {NULL};
8543     const PetscInt **permsC[32] = {NULL};
8544 
8545     for (f = 0; f < numFields; f++) {
8546       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8547       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8548     }
8549     for (p = 0; p < numFPoints; p++) {
8550       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8551       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8552     }
8553     for (p = 0; p < numCPoints; p++) {
8554       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8555       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8556     }
8557     for (f = 0; f < numFields; f++) {
8558       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8559       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8560     }
8561   } else {
8562     const PetscInt **permsF = NULL;
8563     const PetscInt **permsC = NULL;
8564 
8565     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8566     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8567     for (p = 0, off = 0; p < numFPoints; p++) {
8568       const PetscInt *perm = permsF ? permsF[p] : NULL;
8569 
8570       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8571       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8572     }
8573     for (p = 0, off = 0; p < numCPoints; p++) {
8574       const PetscInt *perm = permsC ? permsC[p] : NULL;
8575 
8576       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8577       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8578     }
8579     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8580     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8581   }
8582   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8583   /* TODO: flips */
8584   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8585   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8586   if (ierr) {
8587     PetscMPIInt rank;
8588 
8589     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8590     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8591     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8592     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8593     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8594   }
8595   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8596   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8597   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8598   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8599   PetscFunctionReturn(PETSC_SUCCESS);
8600 }
8601 
8602 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8603 {
8604   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8605   PetscInt       *cpoints      = NULL;
8606   PetscInt        foffsets[32] = {0}, coffsets[32] = {0};
8607   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8608   DMPolytopeType  ct;
8609   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8610 
8611   PetscFunctionBegin;
8612   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8613   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8614   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8615   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8616   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8617   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8618   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8619   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8620   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8621   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8622   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8623   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8624   /* Column indices */
8625   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8626   maxFPoints = numCPoints;
8627   /* Compress out points not in the section */
8628   /*   TODO: Squeeze out points with 0 dof as well */
8629   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8630   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8631     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8632       cpoints[q * 2]     = cpoints[p];
8633       cpoints[q * 2 + 1] = cpoints[p + 1];
8634       ++q;
8635     }
8636   }
8637   numCPoints = q;
8638   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8639     PetscInt fdof;
8640 
8641     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8642     if (!dof) continue;
8643     for (f = 0; f < numFields; ++f) {
8644       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8645       coffsets[f + 1] += fdof;
8646     }
8647     numCIndices += dof;
8648   }
8649   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8650   /* Row indices */
8651   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8652   {
8653     DMPlexTransform tr;
8654     DMPolytopeType *rct;
8655     PetscInt       *rsize, *rcone, *rornt, Nt;
8656 
8657     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8658     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8659     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8660     numSubcells = rsize[Nt - 1];
8661     PetscCall(DMPlexTransformDestroy(&tr));
8662   }
8663   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8664   for (r = 0, q = 0; r < numSubcells; ++r) {
8665     /* TODO Map from coarse to fine cells */
8666     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8667     /* Compress out points not in the section */
8668     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8669     for (p = 0; p < numFPoints * 2; p += 2) {
8670       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8671         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8672         if (!dof) continue;
8673         for (s = 0; s < q; ++s)
8674           if (fpoints[p] == ftotpoints[s * 2]) break;
8675         if (s < q) continue;
8676         ftotpoints[q * 2]     = fpoints[p];
8677         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8678         ++q;
8679       }
8680     }
8681     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8682   }
8683   numFPoints = q;
8684   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8685     PetscInt fdof;
8686 
8687     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8688     if (!dof) continue;
8689     for (f = 0; f < numFields; ++f) {
8690       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8691       foffsets[f + 1] += fdof;
8692     }
8693     numFIndices += dof;
8694   }
8695   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8696 
8697   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8698   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8699   if (numFields) {
8700     const PetscInt **permsF[32] = {NULL};
8701     const PetscInt **permsC[32] = {NULL};
8702 
8703     for (f = 0; f < numFields; f++) {
8704       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8705       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8706     }
8707     for (p = 0; p < numFPoints; p++) {
8708       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8709       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8710     }
8711     for (p = 0; p < numCPoints; p++) {
8712       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8713       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8714     }
8715     for (f = 0; f < numFields; f++) {
8716       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8717       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8718     }
8719   } else {
8720     const PetscInt **permsF = NULL;
8721     const PetscInt **permsC = NULL;
8722 
8723     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8724     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8725     for (p = 0, off = 0; p < numFPoints; p++) {
8726       const PetscInt *perm = permsF ? permsF[p] : NULL;
8727 
8728       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8729       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8730     }
8731     for (p = 0, off = 0; p < numCPoints; p++) {
8732       const PetscInt *perm = permsC ? permsC[p] : NULL;
8733 
8734       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8735       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8736     }
8737     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8738     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8739   }
8740   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8741   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8742   PetscFunctionReturn(PETSC_SUCCESS);
8743 }
8744 
8745 /*@
8746   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8747 
8748   Input Parameter:
8749 . dm - The `DMPLEX` object
8750 
8751   Output Parameter:
8752 . cellHeight - The height of a cell
8753 
8754   Level: developer
8755 
8756 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8757 @*/
8758 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8759 {
8760   DM_Plex *mesh = (DM_Plex *)dm->data;
8761 
8762   PetscFunctionBegin;
8763   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8764   PetscAssertPointer(cellHeight, 2);
8765   *cellHeight = mesh->vtkCellHeight;
8766   PetscFunctionReturn(PETSC_SUCCESS);
8767 }
8768 
8769 /*@
8770   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8771 
8772   Input Parameters:
8773 + dm         - The `DMPLEX` object
8774 - cellHeight - The height of a cell
8775 
8776   Level: developer
8777 
8778 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8779 @*/
8780 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8781 {
8782   DM_Plex *mesh = (DM_Plex *)dm->data;
8783 
8784   PetscFunctionBegin;
8785   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8786   mesh->vtkCellHeight = cellHeight;
8787   PetscFunctionReturn(PETSC_SUCCESS);
8788 }
8789 
8790 /*@
8791   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8792 
8793   Input Parameters:
8794 + dm - The `DMPLEX` object
8795 - ct - The `DMPolytopeType` of the cell
8796 
8797   Output Parameters:
8798 + start - The first cell of this type, or `NULL`
8799 - end   - The upper bound on this celltype, or `NULL`
8800 
8801   Level: advanced
8802 
8803 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8804 @*/
8805 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PeOp PetscInt *start, PeOp PetscInt *end)
8806 {
8807   DM_Plex *mesh = (DM_Plex *)dm->data;
8808   DMLabel  label;
8809   PetscInt pStart, pEnd;
8810 
8811   PetscFunctionBegin;
8812   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8813   if (start) {
8814     PetscAssertPointer(start, 3);
8815     *start = 0;
8816   }
8817   if (end) {
8818     PetscAssertPointer(end, 4);
8819     *end = 0;
8820   }
8821   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8822   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8823   if (mesh->tr) {
8824     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8825   } else {
8826     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8827     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8828     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8829   }
8830   PetscFunctionReturn(PETSC_SUCCESS);
8831 }
8832 
8833 /*@
8834   DMPlexGetDepthStratumGlobalSize - Get the global size for a given depth stratum
8835 
8836   Input Parameters:
8837 + dm    - The `DMPLEX` object
8838 - depth - The depth for the given point stratum
8839 
8840   Output Parameter:
8841 . gsize - The global number of points in the stratum
8842 
8843   Level: advanced
8844 
8845 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8846 @*/
8847 PetscErrorCode DMPlexGetDepthStratumGlobalSize(DM dm, PetscInt depth, PetscInt *gsize)
8848 {
8849   PetscSF         sf;
8850   const PetscInt *leaves;
8851   PetscInt        Nl, loc, start, end, lsize = 0;
8852 
8853   PetscFunctionBegin;
8854   PetscCall(DMGetPointSF(dm, &sf));
8855   PetscCall(PetscSFGetGraph(sf, NULL, &Nl, &leaves, NULL));
8856   PetscCall(DMPlexGetDepthStratum(dm, depth, &start, &end));
8857   for (PetscInt p = start; p < end; ++p) {
8858     PetscCall(PetscFindInt(p, Nl, leaves, &loc));
8859     if (loc < 0) ++lsize;
8860   }
8861   PetscCallMPI(MPI_Allreduce(&lsize, gsize, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
8862   PetscFunctionReturn(PETSC_SUCCESS);
8863 }
8864 
8865 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8866 {
8867   PetscSection section, globalSection;
8868   PetscInt    *numbers, p;
8869 
8870   PetscFunctionBegin;
8871   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8872   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8873   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8874   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8875   PetscCall(PetscSectionSetUp(section));
8876   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &globalSection));
8877   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8878   for (p = pStart; p < pEnd; ++p) {
8879     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8880     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8881     else numbers[p - pStart] += shift;
8882   }
8883   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8884   if (globalSize) {
8885     PetscLayout layout;
8886     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8887     PetscCall(PetscLayoutGetSize(layout, globalSize));
8888     PetscCall(PetscLayoutDestroy(&layout));
8889   }
8890   PetscCall(PetscSectionDestroy(&section));
8891   PetscCall(PetscSectionDestroy(&globalSection));
8892   PetscFunctionReturn(PETSC_SUCCESS);
8893 }
8894 
8895 /*@
8896   DMPlexCreateCellNumbering - Get a global cell numbering for all cells on this process
8897 
8898   Input Parameters:
8899 + dm         - The `DMPLEX` object
8900 - includeAll - Whether to include all cells, or just the simplex and box cells
8901 
8902   Output Parameter:
8903 . globalCellNumbers - Global cell numbers for all cells on this process
8904 
8905   Level: developer
8906 
8907 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`
8908 @*/
8909 PetscErrorCode DMPlexCreateCellNumbering(DM dm, PetscBool includeAll, IS *globalCellNumbers)
8910 {
8911   PetscInt cellHeight, cStart, cEnd;
8912 
8913   PetscFunctionBegin;
8914   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8915   if (includeAll) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8916   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8917   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8918   PetscFunctionReturn(PETSC_SUCCESS);
8919 }
8920 
8921 /*@
8922   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8923 
8924   Input Parameter:
8925 . dm - The `DMPLEX` object
8926 
8927   Output Parameter:
8928 . globalCellNumbers - Global cell numbers for all cells on this process
8929 
8930   Level: developer
8931 
8932 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateCellNumbering()`, `DMPlexGetVertexNumbering()`
8933 @*/
8934 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8935 {
8936   DM_Plex *mesh = (DM_Plex *)dm->data;
8937 
8938   PetscFunctionBegin;
8939   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8940   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8941   *globalCellNumbers = mesh->globalCellNumbers;
8942   PetscFunctionReturn(PETSC_SUCCESS);
8943 }
8944 
8945 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8946 {
8947   PetscInt vStart, vEnd;
8948 
8949   PetscFunctionBegin;
8950   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8951   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8952   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8953   PetscFunctionReturn(PETSC_SUCCESS);
8954 }
8955 
8956 /*@
8957   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8958 
8959   Input Parameter:
8960 . dm - The `DMPLEX` object
8961 
8962   Output Parameter:
8963 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8964 
8965   Level: developer
8966 
8967 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8968 @*/
8969 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8970 {
8971   DM_Plex *mesh = (DM_Plex *)dm->data;
8972 
8973   PetscFunctionBegin;
8974   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8975   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8976   *globalVertexNumbers = mesh->globalVertexNumbers;
8977   PetscFunctionReturn(PETSC_SUCCESS);
8978 }
8979 
8980 /*@
8981   DMPlexCreatePointNumbering - Create a global numbering for all points.
8982 
8983   Collective
8984 
8985   Input Parameter:
8986 . dm - The `DMPLEX` object
8987 
8988   Output Parameter:
8989 . globalPointNumbers - Global numbers for all points on this process
8990 
8991   Level: developer
8992 
8993   Notes:
8994   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8995   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8996   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8997   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8998 
8999   The partitioned mesh is
9000   ```
9001   (2)--0--(3)--1--(4)    (1)--0--(2)
9002   ```
9003   and its global numbering is
9004   ```
9005   (3)--0--(4)--1--(5)--2--(6)
9006   ```
9007   Then the global numbering is provided as
9008   ```
9009   [0] Number of indices in set 5
9010   [0] 0 0
9011   [0] 1 1
9012   [0] 2 3
9013   [0] 3 4
9014   [0] 4 -6
9015   [1] Number of indices in set 3
9016   [1] 0 2
9017   [1] 1 5
9018   [1] 2 6
9019   ```
9020 
9021 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
9022 @*/
9023 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
9024 {
9025   IS        nums[4];
9026   PetscInt  depths[4], gdepths[4], starts[4];
9027   PetscInt  depth, d, shift = 0;
9028   PetscBool empty = PETSC_FALSE;
9029 
9030   PetscFunctionBegin;
9031   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9032   PetscCall(DMPlexGetDepth(dm, &depth));
9033   // For unstratified meshes use dim instead of depth
9034   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
9035   // If any stratum is empty, we must mark all empty
9036   for (d = 0; d <= depth; ++d) {
9037     PetscInt end;
9038 
9039     depths[d] = depth - d;
9040     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
9041     if (!(starts[d] - end)) empty = PETSC_TRUE;
9042   }
9043   if (empty)
9044     for (d = 0; d <= depth; ++d) {
9045       depths[d] = -1;
9046       starts[d] = -1;
9047     }
9048   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
9049   PetscCallMPI(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
9050   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]);
9051   // Note here that 'shift' is collective, so that the numbering is stratified by depth
9052   for (d = 0; d <= depth; ++d) {
9053     PetscInt pStart, pEnd, gsize;
9054 
9055     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
9056     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
9057     shift += gsize;
9058   }
9059   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
9060   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
9061   PetscFunctionReturn(PETSC_SUCCESS);
9062 }
9063 
9064 /*@
9065   DMPlexCreateEdgeNumbering - Create a global numbering for edges.
9066 
9067   Collective
9068 
9069   Input Parameter:
9070 . dm - The `DMPLEX` object
9071 
9072   Output Parameter:
9073 . globalEdgeNumbers - Global numbers for all edges on this process
9074 
9075   Level: developer
9076 
9077   Notes:
9078   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).
9079 
9080 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`, `DMPlexCreatePointNumbering()`
9081 @*/
9082 PetscErrorCode DMPlexCreateEdgeNumbering(DM dm, IS *globalEdgeNumbers)
9083 {
9084   PetscSF  sf;
9085   PetscInt eStart, eEnd;
9086 
9087   PetscFunctionBegin;
9088   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9089   PetscCall(DMGetPointSF(dm, &sf));
9090   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9091   PetscCall(DMPlexCreateNumbering_Plex(dm, eStart, eEnd, 0, NULL, sf, globalEdgeNumbers));
9092   PetscFunctionReturn(PETSC_SUCCESS);
9093 }
9094 
9095 /*@
9096   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
9097 
9098   Input Parameter:
9099 . dm - The `DMPLEX` object
9100 
9101   Output Parameter:
9102 . ranks - The rank field
9103 
9104   Options Database Key:
9105 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
9106 
9107   Level: intermediate
9108 
9109 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
9110 @*/
9111 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
9112 {
9113   DM             rdm;
9114   PetscFE        fe;
9115   PetscScalar   *r;
9116   PetscMPIInt    rank;
9117   DMPolytopeType ct;
9118   PetscInt       dim, cStart, cEnd, c;
9119   PetscBool      simplex;
9120 
9121   PetscFunctionBeginUser;
9122   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9123   PetscAssertPointer(ranks, 2);
9124   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
9125   PetscCall(DMClone(dm, &rdm));
9126   PetscCall(DMGetDimension(rdm, &dim));
9127   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
9128   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
9129   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
9130   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
9131   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
9132   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9133   PetscCall(PetscFEDestroy(&fe));
9134   PetscCall(DMCreateDS(rdm));
9135   PetscCall(DMCreateGlobalVector(rdm, ranks));
9136   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
9137   PetscCall(VecGetArray(*ranks, &r));
9138   for (c = cStart; c < cEnd; ++c) {
9139     PetscScalar *lr;
9140 
9141     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
9142     if (lr) *lr = rank;
9143   }
9144   PetscCall(VecRestoreArray(*ranks, &r));
9145   PetscCall(DMDestroy(&rdm));
9146   PetscFunctionReturn(PETSC_SUCCESS);
9147 }
9148 
9149 /*@
9150   DMPlexCreateLabelField - Create a field whose value is the label value for that point
9151 
9152   Input Parameters:
9153 + dm    - The `DMPLEX`
9154 - label - The `DMLabel`
9155 
9156   Output Parameter:
9157 . val - The label value field
9158 
9159   Options Database Key:
9160 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
9161 
9162   Level: intermediate
9163 
9164 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
9165 @*/
9166 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
9167 {
9168   DM             rdm, plex;
9169   Vec            lval;
9170   PetscSection   section;
9171   PetscFE        fe;
9172   PetscScalar   *v;
9173   PetscInt       dim, pStart, pEnd, p, cStart;
9174   DMPolytopeType ct;
9175   char           name[PETSC_MAX_PATH_LEN];
9176   const char    *lname, *prefix;
9177 
9178   PetscFunctionBeginUser;
9179   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9180   PetscAssertPointer(label, 2);
9181   PetscAssertPointer(val, 3);
9182   PetscCall(DMClone(dm, &rdm));
9183   PetscCall(DMConvert(rdm, DMPLEX, &plex));
9184   PetscCall(DMPlexGetHeightStratum(plex, 0, &cStart, NULL));
9185   PetscCall(DMPlexGetCellType(plex, cStart, &ct));
9186   PetscCall(DMDestroy(&plex));
9187   PetscCall(DMGetDimension(rdm, &dim));
9188   PetscCall(DMGetOptionsPrefix(dm, &prefix));
9189   PetscCall(PetscObjectGetName((PetscObject)label, &lname));
9190   PetscCall(PetscSNPrintf(name, sizeof(name), "%s%s_", prefix ? prefix : "", lname));
9191   PetscCall(PetscFECreateByCell(PETSC_COMM_SELF, dim, 1, ct, name, -1, &fe));
9192   PetscCall(PetscObjectSetName((PetscObject)fe, ""));
9193   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9194   PetscCall(PetscFEDestroy(&fe));
9195   PetscCall(DMCreateDS(rdm));
9196   PetscCall(DMCreateGlobalVector(rdm, val));
9197   PetscCall(DMCreateLocalVector(rdm, &lval));
9198   PetscCall(PetscObjectSetName((PetscObject)*val, lname));
9199   PetscCall(DMGetLocalSection(rdm, &section));
9200   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
9201   PetscCall(VecGetArray(lval, &v));
9202   for (p = pStart; p < pEnd; ++p) {
9203     PetscInt cval, dof, off;
9204 
9205     PetscCall(PetscSectionGetDof(section, p, &dof));
9206     if (!dof) continue;
9207     PetscCall(DMLabelGetValue(label, p, &cval));
9208     PetscCall(PetscSectionGetOffset(section, p, &off));
9209     for (PetscInt d = 0; d < dof; d++) v[off + d] = cval;
9210   }
9211   PetscCall(VecRestoreArray(lval, &v));
9212   PetscCall(DMLocalToGlobal(rdm, lval, INSERT_VALUES, *val));
9213   PetscCall(VecDestroy(&lval));
9214   PetscCall(DMDestroy(&rdm));
9215   PetscFunctionReturn(PETSC_SUCCESS);
9216 }
9217 
9218 /*@
9219   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
9220 
9221   Input Parameter:
9222 . dm - The `DMPLEX` object
9223 
9224   Level: developer
9225 
9226   Notes:
9227   This is a useful diagnostic when creating meshes programmatically.
9228 
9229   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9230 
9231 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9232 @*/
9233 PetscErrorCode DMPlexCheckSymmetry(DM dm)
9234 {
9235   PetscSection    coneSection, supportSection;
9236   const PetscInt *cone, *support;
9237   PetscInt        coneSize, c, supportSize, s;
9238   PetscInt        pStart, pEnd, p, pp, csize, ssize;
9239   PetscBool       storagecheck = PETSC_TRUE;
9240 
9241   PetscFunctionBegin;
9242   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9243   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
9244   PetscCall(DMPlexGetConeSection(dm, &coneSection));
9245   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
9246   /* Check that point p is found in the support of its cone points, and vice versa */
9247   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9248   for (p = pStart; p < pEnd; ++p) {
9249     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
9250     PetscCall(DMPlexGetCone(dm, p, &cone));
9251     for (c = 0; c < coneSize; ++c) {
9252       PetscBool dup = PETSC_FALSE;
9253       PetscInt  d;
9254       for (d = c - 1; d >= 0; --d) {
9255         if (cone[c] == cone[d]) {
9256           dup = PETSC_TRUE;
9257           break;
9258         }
9259       }
9260       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
9261       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
9262       for (s = 0; s < supportSize; ++s) {
9263         if (support[s] == p) break;
9264       }
9265       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
9266         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
9267         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
9268         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9269         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
9270         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
9271         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9272         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]);
9273         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
9274       }
9275     }
9276     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
9277     if (p != pp) {
9278       storagecheck = PETSC_FALSE;
9279       continue;
9280     }
9281     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
9282     PetscCall(DMPlexGetSupport(dm, p, &support));
9283     for (s = 0; s < supportSize; ++s) {
9284       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
9285       PetscCall(DMPlexGetCone(dm, support[s], &cone));
9286       for (c = 0; c < coneSize; ++c) {
9287         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
9288         if (cone[c] != pp) {
9289           c = 0;
9290           break;
9291         }
9292         if (cone[c] == p) break;
9293       }
9294       if (c >= coneSize) {
9295         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
9296         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
9297         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9298         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
9299         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
9300         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9301         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
9302       }
9303     }
9304   }
9305   if (storagecheck) {
9306     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
9307     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
9308     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
9309   }
9310   PetscFunctionReturn(PETSC_SUCCESS);
9311 }
9312 
9313 /*
9314   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.
9315 */
9316 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
9317 {
9318   DMPolytopeType  cct;
9319   PetscInt        ptpoints[4];
9320   const PetscInt *cone, *ccone, *ptcone;
9321   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
9322 
9323   PetscFunctionBegin;
9324   *unsplit = 0;
9325   switch (ct) {
9326   case DM_POLYTOPE_POINT_PRISM_TENSOR:
9327     ptpoints[npt++] = c;
9328     break;
9329   case DM_POLYTOPE_SEG_PRISM_TENSOR:
9330     PetscCall(DMPlexGetCone(dm, c, &cone));
9331     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9332     for (cp = 0; cp < coneSize; ++cp) {
9333       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
9334       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
9335     }
9336     break;
9337   case DM_POLYTOPE_TRI_PRISM_TENSOR:
9338   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9339     PetscCall(DMPlexGetCone(dm, c, &cone));
9340     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9341     for (cp = 0; cp < coneSize; ++cp) {
9342       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
9343       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
9344       for (ccp = 0; ccp < cconeSize; ++ccp) {
9345         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
9346         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
9347           PetscInt p;
9348           for (p = 0; p < npt; ++p)
9349             if (ptpoints[p] == ccone[ccp]) break;
9350           if (p == npt) ptpoints[npt++] = ccone[ccp];
9351         }
9352       }
9353     }
9354     break;
9355   default:
9356     break;
9357   }
9358   for (pt = 0; pt < npt; ++pt) {
9359     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
9360     if (ptcone[0] == ptcone[1]) ++(*unsplit);
9361   }
9362   PetscFunctionReturn(PETSC_SUCCESS);
9363 }
9364 
9365 /*@
9366   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
9367 
9368   Input Parameters:
9369 + dm         - The `DMPLEX` object
9370 - cellHeight - Normally 0
9371 
9372   Level: developer
9373 
9374   Notes:
9375   This is a useful diagnostic when creating meshes programmatically.
9376   Currently applicable only to homogeneous simplex or tensor meshes.
9377 
9378   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9379 
9380 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9381 @*/
9382 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
9383 {
9384   DMPlexInterpolatedFlag interp;
9385   DMPolytopeType         ct;
9386   PetscInt               vStart, vEnd, cStart, cEnd, c;
9387 
9388   PetscFunctionBegin;
9389   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9390   PetscCall(DMPlexIsInterpolated(dm, &interp));
9391   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9392   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9393   for (c = cStart; c < cEnd; ++c) {
9394     PetscInt *closure = NULL;
9395     PetscInt  coneSize, closureSize, cl, Nv = 0;
9396 
9397     PetscCall(DMPlexGetCellType(dm, c, &ct));
9398     if (ct == DM_POLYTOPE_UNKNOWN) continue;
9399     if (interp == DMPLEX_INTERPOLATED_FULL) {
9400       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9401       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));
9402     }
9403     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9404     for (cl = 0; cl < closureSize * 2; cl += 2) {
9405       const PetscInt p = closure[cl];
9406       if ((p >= vStart) && (p < vEnd)) ++Nv;
9407     }
9408     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9409     /* Special Case: Tensor faces with identified vertices */
9410     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
9411       PetscInt unsplit;
9412 
9413       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9414       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
9415     }
9416     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));
9417   }
9418   PetscFunctionReturn(PETSC_SUCCESS);
9419 }
9420 
9421 /*@
9422   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
9423 
9424   Collective
9425 
9426   Input Parameters:
9427 + dm         - The `DMPLEX` object
9428 - cellHeight - Normally 0
9429 
9430   Level: developer
9431 
9432   Notes:
9433   This is a useful diagnostic when creating meshes programmatically.
9434   This routine is only relevant for meshes that are fully interpolated across all ranks.
9435   It will error out if a partially interpolated mesh is given on some rank.
9436   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
9437 
9438   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9439 
9440 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
9441 @*/
9442 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
9443 {
9444   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
9445   DMPlexInterpolatedFlag interpEnum;
9446 
9447   PetscFunctionBegin;
9448   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9449   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
9450   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
9451   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
9452     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
9453     PetscFunctionReturn(PETSC_SUCCESS);
9454   }
9455 
9456   PetscCall(DMGetDimension(dm, &dim));
9457   PetscCall(DMPlexGetDepth(dm, &depth));
9458   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9459   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
9460     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
9461     for (c = cStart; c < cEnd; ++c) {
9462       const PetscInt       *cone, *ornt, *faceSizes, *faces;
9463       const DMPolytopeType *faceTypes;
9464       DMPolytopeType        ct;
9465       PetscInt              numFaces, coneSize, f;
9466       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
9467 
9468       PetscCall(DMPlexGetCellType(dm, c, &ct));
9469       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9470       if (unsplit) continue;
9471       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9472       PetscCall(DMPlexGetCone(dm, c, &cone));
9473       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
9474       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9475       for (cl = 0; cl < closureSize * 2; cl += 2) {
9476         const PetscInt p = closure[cl];
9477         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
9478       }
9479       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9480       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);
9481       for (f = 0; f < numFaces; ++f) {
9482         DMPolytopeType fct;
9483         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
9484 
9485         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
9486         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
9487         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
9488           const PetscInt p = fclosure[cl];
9489           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
9490         }
9491         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]);
9492         for (v = 0; v < fnumCorners; ++v) {
9493           if (fclosure[v] != faces[fOff + v]) {
9494             PetscInt v1;
9495 
9496             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
9497             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
9498             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
9499             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
9500             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9501             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]);
9502           }
9503         }
9504         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
9505         fOff += faceSizes[f];
9506       }
9507       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9508       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9509     }
9510   }
9511   PetscFunctionReturn(PETSC_SUCCESS);
9512 }
9513 
9514 /*@
9515   DMPlexCheckGeometry - Check the geometry of mesh cells
9516 
9517   Input Parameter:
9518 . dm - The `DMPLEX` object
9519 
9520   Level: developer
9521 
9522   Notes:
9523   This is a useful diagnostic when creating meshes programmatically.
9524 
9525   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9526 
9527 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9528 @*/
9529 PetscErrorCode DMPlexCheckGeometry(DM dm)
9530 {
9531   Vec       coordinates;
9532   PetscReal detJ, J[9], refVol = 1.0;
9533   PetscReal vol;
9534   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9535 
9536   PetscFunctionBegin;
9537   PetscCall(DMGetDimension(dm, &dim));
9538   PetscCall(DMGetCoordinateDim(dm, &dE));
9539   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9540   PetscCall(DMPlexGetDepth(dm, &depth));
9541   for (d = 0; d < dim; ++d) refVol *= 2.0;
9542   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9543   /* Make sure local coordinates are created, because that step is collective */
9544   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9545   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9546   for (c = cStart; c < cEnd; ++c) {
9547     DMPolytopeType ct;
9548     PetscInt       unsplit;
9549     PetscBool      ignoreZeroVol = PETSC_FALSE;
9550 
9551     PetscCall(DMPlexGetCellType(dm, c, &ct));
9552     switch (ct) {
9553     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9554     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9555     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9556       ignoreZeroVol = PETSC_TRUE;
9557       break;
9558     default:
9559       break;
9560     }
9561     switch (ct) {
9562     case DM_POLYTOPE_TRI_PRISM:
9563     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9564     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9565     case DM_POLYTOPE_PYRAMID:
9566       continue;
9567     default:
9568       break;
9569     }
9570     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9571     if (unsplit) continue;
9572     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9573     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);
9574     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9575     /* This should work with periodicity since DG coordinates should be used */
9576     if (depth > 1) {
9577       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9578       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);
9579       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9580     }
9581   }
9582   PetscFunctionReturn(PETSC_SUCCESS);
9583 }
9584 
9585 /*@
9586   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9587 
9588   Collective
9589 
9590   Input Parameters:
9591 + dm              - The `DMPLEX` object
9592 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9593 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9594 
9595   Level: developer
9596 
9597   Notes:
9598   This is mainly intended for debugging/testing purposes.
9599 
9600   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9601 
9602   Extra roots can come from periodic cuts, where additional points appear on the boundary
9603 
9604 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9605 @*/
9606 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9607 {
9608   PetscInt           l, nleaves, nroots, overlap;
9609   const PetscInt    *locals;
9610   const PetscSFNode *remotes;
9611   PetscBool          distributed;
9612   MPI_Comm           comm;
9613   PetscMPIInt        rank;
9614 
9615   PetscFunctionBegin;
9616   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9617   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9618   else pointSF = dm->sf;
9619   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9620   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9621   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9622   {
9623     PetscMPIInt mpiFlag;
9624 
9625     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9626     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9627   }
9628   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9629   PetscCall(DMPlexIsDistributed(dm, &distributed));
9630   if (!distributed) {
9631     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);
9632     PetscFunctionReturn(PETSC_SUCCESS);
9633   }
9634   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);
9635   PetscCall(DMPlexGetOverlap(dm, &overlap));
9636 
9637   /* Check SF graph is compatible with DMPlex chart */
9638   {
9639     PetscInt pStart, pEnd, maxLeaf;
9640 
9641     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9642     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9643     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9644     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9645   }
9646 
9647   /* Check there are no cells in interface */
9648   if (!overlap) {
9649     PetscInt cellHeight, cStart, cEnd;
9650 
9651     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9652     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9653     for (l = 0; l < nleaves; ++l) {
9654       const PetscInt point = locals ? locals[l] : l;
9655 
9656       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9657     }
9658   }
9659 
9660   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9661   {
9662     const PetscInt *rootdegree;
9663 
9664     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9665     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9666     for (l = 0; l < nleaves; ++l) {
9667       const PetscInt  point = locals ? locals[l] : l;
9668       const PetscInt *cone;
9669       PetscInt        coneSize, c, idx;
9670 
9671       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9672       PetscCall(DMPlexGetCone(dm, point, &cone));
9673       for (c = 0; c < coneSize; ++c) {
9674         if (!rootdegree[cone[c]]) {
9675           if (locals) {
9676             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9677           } else {
9678             idx = (cone[c] < nleaves) ? cone[c] : -1;
9679           }
9680           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9681         }
9682       }
9683     }
9684   }
9685   PetscFunctionReturn(PETSC_SUCCESS);
9686 }
9687 
9688 /*@
9689   DMPlexCheckOrphanVertices - Check that no vertices are disconnected from the mesh, unless the mesh only consists of disconnected vertices.
9690 
9691   Collective
9692 
9693   Input Parameter:
9694 . dm - The `DMPLEX` object
9695 
9696   Level: developer
9697 
9698   Notes:
9699   This is mainly intended for debugging/testing purposes.
9700 
9701   Other cell types which are disconnected would be caught by the symmetry and face checks.
9702 
9703   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9704 
9705 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheck()`, `DMSetFromOptions()`
9706 @*/
9707 PetscErrorCode DMPlexCheckOrphanVertices(DM dm)
9708 {
9709   PetscInt pStart, pEnd, vStart, vEnd;
9710 
9711   PetscFunctionBegin;
9712   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9713   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9714   if (pStart == vStart && pEnd == vEnd) PetscFunctionReturn(PETSC_SUCCESS);
9715   for (PetscInt v = vStart; v < vEnd; ++v) {
9716     PetscInt suppSize;
9717 
9718     PetscCall(DMPlexGetSupportSize(dm, v, &suppSize));
9719     PetscCheck(suppSize, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Vertex %" PetscInt_FMT " is disconnected from the mesh", v);
9720   }
9721   PetscFunctionReturn(PETSC_SUCCESS);
9722 }
9723 
9724 /*@
9725   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9726 
9727   Input Parameter:
9728 . dm - The `DMPLEX` object
9729 
9730   Level: developer
9731 
9732   Notes:
9733   This is a useful diagnostic when creating meshes programmatically.
9734 
9735   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9736 
9737   Currently does not include `DMPlexCheckCellShape()`.
9738 
9739 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9740 @*/
9741 PetscErrorCode DMPlexCheck(DM dm)
9742 {
9743   PetscInt cellHeight;
9744 
9745   PetscFunctionBegin;
9746   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9747   PetscCall(DMPlexCheckSymmetry(dm));
9748   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9749   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9750   PetscCall(DMPlexCheckGeometry(dm));
9751   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9752   PetscCall(DMPlexCheckInterfaceCones(dm));
9753   PetscCall(DMPlexCheckOrphanVertices(dm));
9754   PetscFunctionReturn(PETSC_SUCCESS);
9755 }
9756 
9757 typedef struct cell_stats {
9758   PetscReal min, max, sum, squaresum;
9759   PetscInt  count;
9760 } cell_stats_t;
9761 
9762 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9763 {
9764   PetscInt i, N = *len;
9765 
9766   for (i = 0; i < N; i++) {
9767     cell_stats_t *A = (cell_stats_t *)a;
9768     cell_stats_t *B = (cell_stats_t *)b;
9769 
9770     B->min = PetscMin(A->min, B->min);
9771     B->max = PetscMax(A->max, B->max);
9772     B->sum += A->sum;
9773     B->squaresum += A->squaresum;
9774     B->count += A->count;
9775   }
9776 }
9777 
9778 /*@
9779   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9780 
9781   Collective
9782 
9783   Input Parameters:
9784 + dm        - The `DMPLEX` object
9785 . output    - If true, statistics will be displayed on `stdout`
9786 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9787 
9788   Level: developer
9789 
9790   Notes:
9791   This is mainly intended for debugging/testing purposes.
9792 
9793   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9794 
9795 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9796 @*/
9797 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9798 {
9799   DM           dmCoarse;
9800   cell_stats_t stats, globalStats;
9801   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9802   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9803   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9804   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9805   PetscMPIInt  rank, size;
9806 
9807   PetscFunctionBegin;
9808   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9809   stats.min = PETSC_MAX_REAL;
9810   stats.max = PETSC_MIN_REAL;
9811   stats.sum = stats.squaresum = 0.;
9812   stats.count                 = 0;
9813 
9814   PetscCallMPI(MPI_Comm_size(comm, &size));
9815   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9816   PetscCall(DMGetCoordinateDim(dm, &cdim));
9817   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9818   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9819   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9820   for (c = cStart; c < cEnd; c++) {
9821     PetscInt  i;
9822     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9823 
9824     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9825     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9826     for (i = 0; i < PetscSqr(cdim); ++i) {
9827       frobJ += J[i] * J[i];
9828       frobInvJ += invJ[i] * invJ[i];
9829     }
9830     cond2 = frobJ * frobInvJ;
9831     cond  = PetscSqrtReal(cond2);
9832 
9833     stats.min = PetscMin(stats.min, cond);
9834     stats.max = PetscMax(stats.max, cond);
9835     stats.sum += cond;
9836     stats.squaresum += cond2;
9837     stats.count++;
9838     if (output && cond > limit) {
9839       PetscSection coordSection;
9840       Vec          coordsLocal;
9841       PetscScalar *coords = NULL;
9842       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9843 
9844       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9845       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9846       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9847       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9848       for (i = 0; i < Nv / cdim; ++i) {
9849         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9850         for (d = 0; d < cdim; ++d) {
9851           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9852           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9853         }
9854         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9855       }
9856       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9857       for (cl = 0; cl < clSize * 2; cl += 2) {
9858         const PetscInt edge = closure[cl];
9859 
9860         if ((edge >= eStart) && (edge < eEnd)) {
9861           PetscReal len;
9862 
9863           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9864           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9865         }
9866       }
9867       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9868       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9869     }
9870   }
9871   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9872 
9873   if (size > 1) {
9874     PetscMPIInt  blockLengths[2] = {4, 1};
9875     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9876     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9877     MPI_Op       statReduce;
9878 
9879     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9880     PetscCallMPI(MPI_Type_commit(&statType));
9881     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9882     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9883     PetscCallMPI(MPI_Op_free(&statReduce));
9884     PetscCallMPI(MPI_Type_free(&statType));
9885   } else {
9886     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9887   }
9888   if (rank == 0) {
9889     count = globalStats.count;
9890     min   = globalStats.min;
9891     max   = globalStats.max;
9892     mean  = globalStats.sum / globalStats.count;
9893     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9894   }
9895 
9896   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));
9897   PetscCall(PetscFree2(J, invJ));
9898 
9899   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9900   if (dmCoarse) {
9901     PetscBool isplex;
9902 
9903     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9904     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9905   }
9906   PetscFunctionReturn(PETSC_SUCCESS);
9907 }
9908 
9909 /*@
9910   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9911   orthogonal quality below given tolerance.
9912 
9913   Collective
9914 
9915   Input Parameters:
9916 + dm   - The `DMPLEX` object
9917 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9918 - atol - [0, 1] Absolute tolerance for tagging cells.
9919 
9920   Output Parameters:
9921 + OrthQual      - `Vec` containing orthogonal quality per cell
9922 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9923 
9924   Options Database Keys:
9925 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9926 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9927 
9928   Level: intermediate
9929 
9930   Notes:
9931   Orthogonal quality is given by the following formula\:
9932 
9933   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9934 
9935   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
9936   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9937   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9938   calculating the cosine of the angle between these vectors.
9939 
9940   Orthogonal quality ranges from 1 (best) to 0 (worst).
9941 
9942   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9943   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9944 
9945   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9946 
9947 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9948 @*/
9949 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PeOp PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9950 {
9951   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9952   PetscInt              *idx;
9953   PetscScalar           *oqVals;
9954   const PetscScalar     *cellGeomArr, *faceGeomArr;
9955   PetscReal             *ci, *fi, *Ai;
9956   MPI_Comm               comm;
9957   Vec                    cellgeom, facegeom;
9958   DM                     dmFace, dmCell;
9959   IS                     glob;
9960   ISLocalToGlobalMapping ltog;
9961   PetscViewer            vwr;
9962 
9963   PetscFunctionBegin;
9964   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9965   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9966   PetscAssertPointer(OrthQual, 4);
9967   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9968   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9969   PetscCall(DMGetDimension(dm, &nc));
9970   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9971   {
9972     DMPlexInterpolatedFlag interpFlag;
9973 
9974     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9975     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9976       PetscMPIInt rank;
9977 
9978       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9979       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9980     }
9981   }
9982   if (OrthQualLabel) {
9983     PetscAssertPointer(OrthQualLabel, 5);
9984     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9985     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9986   } else {
9987     *OrthQualLabel = NULL;
9988   }
9989   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9990   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9991   PetscCall(DMPlexCreateCellNumbering(dm, PETSC_TRUE, &glob));
9992   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9993   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9994   PetscCall(VecCreate(comm, OrthQual));
9995   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9996   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9997   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9998   PetscCall(VecSetUp(*OrthQual));
9999   PetscCall(ISDestroy(&glob));
10000   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
10001   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
10002   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
10003   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
10004   PetscCall(VecGetDM(cellgeom, &dmCell));
10005   PetscCall(VecGetDM(facegeom, &dmFace));
10006   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
10007   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
10008     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
10009     PetscInt         cellarr[2], *adj = NULL;
10010     PetscScalar     *cArr, *fArr;
10011     PetscReal        minvalc = 1.0, minvalf = 1.0;
10012     PetscFVCellGeom *cg;
10013 
10014     idx[cellIter] = cell - cStart;
10015     cellarr[0]    = cell;
10016     /* Make indexing into cellGeom easier */
10017     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
10018     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
10019     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
10020     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
10021     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
10022       PetscInt         i;
10023       const PetscInt   neigh  = adj[cellneigh];
10024       PetscReal        normci = 0, normfi = 0, normai = 0;
10025       PetscFVCellGeom *cgneigh;
10026       PetscFVFaceGeom *fg;
10027 
10028       /* Don't count ourselves in the neighbor list */
10029       if (neigh == cell) continue;
10030       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
10031       cellarr[1] = neigh;
10032       {
10033         PetscInt        numcovpts;
10034         const PetscInt *covpts;
10035 
10036         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
10037         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
10038         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
10039       }
10040 
10041       /* Compute c_i, f_i and their norms */
10042       for (i = 0; i < nc; i++) {
10043         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
10044         fi[i] = fg->centroid[i] - cg->centroid[i];
10045         Ai[i] = fg->normal[i];
10046         normci += PetscPowReal(ci[i], 2);
10047         normfi += PetscPowReal(fi[i], 2);
10048         normai += PetscPowReal(Ai[i], 2);
10049       }
10050       normci = PetscSqrtReal(normci);
10051       normfi = PetscSqrtReal(normfi);
10052       normai = PetscSqrtReal(normai);
10053 
10054       /* Normalize and compute for each face-cell-normal pair */
10055       for (i = 0; i < nc; i++) {
10056         ci[i] = ci[i] / normci;
10057         fi[i] = fi[i] / normfi;
10058         Ai[i] = Ai[i] / normai;
10059         /* PetscAbs because I don't know if normals are guaranteed to point out */
10060         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
10061         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
10062       }
10063       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
10064       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
10065     }
10066     PetscCall(PetscFree(adj));
10067     PetscCall(PetscFree2(cArr, fArr));
10068     /* Defer to cell if they're equal */
10069     oqVals[cellIter] = PetscMin(minvalf, minvalc);
10070     if (OrthQualLabel) {
10071       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
10072     }
10073   }
10074   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
10075   PetscCall(VecAssemblyBegin(*OrthQual));
10076   PetscCall(VecAssemblyEnd(*OrthQual));
10077   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
10078   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
10079   PetscCall(PetscOptionsCreateViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
10080   if (OrthQualLabel) {
10081     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
10082   }
10083   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
10084   PetscCall(PetscViewerDestroy(&vwr));
10085   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
10086   PetscFunctionReturn(PETSC_SUCCESS);
10087 }
10088 
10089 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
10090  * interpolator construction */
10091 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
10092 {
10093   PetscSection section, newSection, gsection;
10094   PetscSF      sf;
10095   PetscBool    hasConstraints, ghasConstraints;
10096 
10097   PetscFunctionBegin;
10098   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10099   PetscAssertPointer(odm, 2);
10100   PetscCall(DMGetLocalSection(dm, &section));
10101   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
10102   PetscCallMPI(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
10103   if (!ghasConstraints) {
10104     PetscCall(PetscObjectReference((PetscObject)dm));
10105     *odm = dm;
10106     PetscFunctionReturn(PETSC_SUCCESS);
10107   }
10108   PetscCall(DMClone(dm, odm));
10109   PetscCall(DMCopyFields(dm, PETSC_DETERMINE, PETSC_DETERMINE, *odm));
10110   PetscCall(DMGetLocalSection(*odm, &newSection));
10111   PetscCall(DMGetPointSF(*odm, &sf));
10112   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_TRUE, PETSC_FALSE, &gsection));
10113   PetscCall(DMSetGlobalSection(*odm, gsection));
10114   PetscCall(PetscSectionDestroy(&gsection));
10115   PetscFunctionReturn(PETSC_SUCCESS);
10116 }
10117 
10118 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
10119 {
10120   DM        dmco, dmfo;
10121   Mat       interpo;
10122   Vec       rscale;
10123   Vec       cglobalo, clocal;
10124   Vec       fglobal, fglobalo, flocal;
10125   PetscBool regular;
10126 
10127   PetscFunctionBegin;
10128   PetscCall(DMGetFullDM(dmc, &dmco));
10129   PetscCall(DMGetFullDM(dmf, &dmfo));
10130   PetscCall(DMSetCoarseDM(dmfo, dmco));
10131   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
10132   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
10133   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
10134   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
10135   PetscCall(DMCreateLocalVector(dmc, &clocal));
10136   PetscCall(VecSet(cglobalo, 0.));
10137   PetscCall(VecSet(clocal, 0.));
10138   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
10139   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
10140   PetscCall(DMCreateLocalVector(dmf, &flocal));
10141   PetscCall(VecSet(fglobal, 0.));
10142   PetscCall(VecSet(fglobalo, 0.));
10143   PetscCall(VecSet(flocal, 0.));
10144   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
10145   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
10146   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
10147   PetscCall(MatMult(interpo, cglobalo, fglobalo));
10148   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
10149   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
10150   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
10151   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
10152   *shift = fglobal;
10153   PetscCall(VecDestroy(&flocal));
10154   PetscCall(VecDestroy(&fglobalo));
10155   PetscCall(VecDestroy(&clocal));
10156   PetscCall(VecDestroy(&cglobalo));
10157   PetscCall(VecDestroy(&rscale));
10158   PetscCall(MatDestroy(&interpo));
10159   PetscCall(DMDestroy(&dmfo));
10160   PetscCall(DMDestroy(&dmco));
10161   PetscFunctionReturn(PETSC_SUCCESS);
10162 }
10163 
10164 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
10165 {
10166   PetscObject shifto;
10167   Vec         shift;
10168 
10169   PetscFunctionBegin;
10170   if (!interp) {
10171     Vec rscale;
10172 
10173     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
10174     PetscCall(VecDestroy(&rscale));
10175   } else {
10176     PetscCall(PetscObjectReference((PetscObject)interp));
10177   }
10178   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
10179   if (!shifto) {
10180     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
10181     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
10182     shifto = (PetscObject)shift;
10183     PetscCall(VecDestroy(&shift));
10184   }
10185   shift = (Vec)shifto;
10186   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
10187   PetscCall(VecAXPY(fineSol, 1.0, shift));
10188   PetscCall(MatDestroy(&interp));
10189   PetscFunctionReturn(PETSC_SUCCESS);
10190 }
10191 
10192 /* Pointwise interpolation
10193      Just code FEM for now
10194      u^f = I u^c
10195      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
10196      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
10197      I_{ij} = psi^f_i phi^c_j
10198 */
10199 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
10200 {
10201   PetscSection gsc, gsf;
10202   PetscInt     m, n;
10203   void        *ctx;
10204   DM           cdm;
10205   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
10206 
10207   PetscFunctionBegin;
10208   PetscCall(DMGetGlobalSection(dmFine, &gsf));
10209   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10210   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10211   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10212 
10213   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
10214   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
10215   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10216   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
10217   PetscCall(DMGetApplicationContext(dmFine, &ctx));
10218 
10219   PetscCall(DMGetCoarseDM(dmFine, &cdm));
10220   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10221   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
10222   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
10223   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
10224   if (scaling) {
10225     /* Use naive scaling */
10226     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
10227   }
10228   PetscFunctionReturn(PETSC_SUCCESS);
10229 }
10230 
10231 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
10232 {
10233   VecScatter ctx;
10234 
10235   PetscFunctionBegin;
10236   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
10237   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
10238   PetscCall(VecScatterDestroy(&ctx));
10239   PetscFunctionReturn(PETSC_SUCCESS);
10240 }
10241 
10242 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[])
10243 {
10244   const PetscInt f  = (PetscInt)PetscRealPart(constants[numConstants]);
10245   const PetscInt Nc = uOff[f + 1] - uOff[f];
10246   for (PetscInt c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
10247 }
10248 
10249 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *lmass, Vec *mass)
10250 {
10251   DM           dmc;
10252   PetscDS      ds;
10253   Vec          ones, locmass;
10254   IS           cellIS;
10255   PetscFormKey key;
10256   PetscInt     depth;
10257 
10258   PetscFunctionBegin;
10259   PetscCall(DMClone(dm, &dmc));
10260   PetscCall(DMCopyDisc(dm, dmc));
10261   PetscCall(DMGetDS(dmc, &ds));
10262   for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10263   if (mass) PetscCall(DMCreateGlobalVector(dm, mass));
10264   if (lmass) PetscCall(DMCreateLocalVector(dm, &locmass));
10265   else PetscCall(DMGetLocalVector(dm, &locmass));
10266   PetscCall(DMGetLocalVector(dm, &ones));
10267   PetscCall(DMPlexGetDepth(dm, &depth));
10268   PetscCall(DMGetStratumIS(dm, "depth", depth, &cellIS));
10269   PetscCall(VecSet(locmass, 0.0));
10270   PetscCall(VecSet(ones, 1.0));
10271   key.label = NULL;
10272   key.value = 0;
10273   key.field = 0;
10274   key.part  = 0;
10275   PetscCall(DMPlexComputeJacobianActionByKey(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
10276   PetscCall(ISDestroy(&cellIS));
10277   if (mass) {
10278     PetscCall(DMLocalToGlobalBegin(dm, locmass, ADD_VALUES, *mass));
10279     PetscCall(DMLocalToGlobalEnd(dm, locmass, ADD_VALUES, *mass));
10280   }
10281   PetscCall(DMRestoreLocalVector(dm, &ones));
10282   if (lmass) *lmass = locmass;
10283   else PetscCall(DMRestoreLocalVector(dm, &locmass));
10284   PetscCall(DMDestroy(&dmc));
10285   PetscFunctionReturn(PETSC_SUCCESS);
10286 }
10287 
10288 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
10289 {
10290   PetscSection gsc, gsf;
10291   PetscInt     m, n;
10292   void        *ctx;
10293   DM           cdm;
10294   PetscBool    regular;
10295 
10296   PetscFunctionBegin;
10297   if (dmFine == dmCoarse) {
10298     DM            dmc;
10299     PetscDS       ds;
10300     PetscWeakForm wf;
10301     Vec           u;
10302     IS            cellIS;
10303     PetscFormKey  key;
10304     PetscInt      depth;
10305 
10306     PetscCall(DMClone(dmFine, &dmc));
10307     PetscCall(DMCopyDisc(dmFine, dmc));
10308     PetscCall(DMGetDS(dmc, &ds));
10309     PetscCall(PetscDSGetWeakForm(ds, &wf));
10310     PetscCall(PetscWeakFormClear(wf));
10311     for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10312     PetscCall(DMCreateMatrix(dmc, mass));
10313     PetscCall(DMGetLocalVector(dmc, &u));
10314     PetscCall(DMPlexGetDepth(dmc, &depth));
10315     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
10316     PetscCall(MatZeroEntries(*mass));
10317     key.label = NULL;
10318     key.value = 0;
10319     key.field = 0;
10320     key.part  = 0;
10321     PetscCall(DMPlexComputeJacobianByKey(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
10322     PetscCall(ISDestroy(&cellIS));
10323     PetscCall(DMRestoreLocalVector(dmc, &u));
10324     PetscCall(DMDestroy(&dmc));
10325   } else {
10326     PetscCall(DMGetGlobalSection(dmFine, &gsf));
10327     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10328     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10329     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10330 
10331     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
10332     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10333     PetscCall(MatSetType(*mass, dmCoarse->mattype));
10334     PetscCall(DMGetApplicationContext(dmFine, &ctx));
10335 
10336     PetscCall(DMGetCoarseDM(dmFine, &cdm));
10337     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10338     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
10339     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
10340   }
10341   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
10342   PetscFunctionReturn(PETSC_SUCCESS);
10343 }
10344 
10345 /*@
10346   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10347 
10348   Input Parameter:
10349 . dm - The `DMPLEX` object
10350 
10351   Output Parameter:
10352 . regular - The flag
10353 
10354   Level: intermediate
10355 
10356 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
10357 @*/
10358 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
10359 {
10360   PetscFunctionBegin;
10361   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10362   PetscAssertPointer(regular, 2);
10363   *regular = ((DM_Plex *)dm->data)->regularRefinement;
10364   PetscFunctionReturn(PETSC_SUCCESS);
10365 }
10366 
10367 /*@
10368   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10369 
10370   Input Parameters:
10371 + dm      - The `DMPLEX` object
10372 - regular - The flag
10373 
10374   Level: intermediate
10375 
10376 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
10377 @*/
10378 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
10379 {
10380   PetscFunctionBegin;
10381   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10382   ((DM_Plex *)dm->data)->regularRefinement = regular;
10383   PetscFunctionReturn(PETSC_SUCCESS);
10384 }
10385 
10386 /*@
10387   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
10388   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
10389 
10390   Not Collective
10391 
10392   Input Parameter:
10393 . dm - The `DMPLEX` object
10394 
10395   Output Parameters:
10396 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
10397 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
10398 
10399   Level: intermediate
10400 
10401 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
10402 @*/
10403 PetscErrorCode DMPlexGetAnchors(DM dm, PeOp PetscSection *anchorSection, PeOp IS *anchorIS)
10404 {
10405   DM_Plex *plex = (DM_Plex *)dm->data;
10406 
10407   PetscFunctionBegin;
10408   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10409   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
10410   if (anchorSection) *anchorSection = plex->anchorSection;
10411   if (anchorIS) *anchorIS = plex->anchorIS;
10412   PetscFunctionReturn(PETSC_SUCCESS);
10413 }
10414 
10415 /*@
10416   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
10417 
10418   Collective
10419 
10420   Input Parameters:
10421 + dm            - The `DMPLEX` object
10422 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
10423                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10424 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10425 
10426   Level: intermediate
10427 
10428   Notes:
10429   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
10430   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
10431   combination of other points' degrees of freedom.
10432 
10433   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
10434   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
10435 
10436   The reference counts of `anchorSection` and `anchorIS` are incremented.
10437 
10438 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
10439 @*/
10440 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
10441 {
10442   DM_Plex    *plex = (DM_Plex *)dm->data;
10443   PetscMPIInt result;
10444 
10445   PetscFunctionBegin;
10446   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10447   if (anchorSection) {
10448     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
10449     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
10450     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
10451   }
10452   if (anchorIS) {
10453     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
10454     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
10455     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
10456   }
10457 
10458   PetscCall(PetscObjectReference((PetscObject)anchorSection));
10459   PetscCall(PetscSectionDestroy(&plex->anchorSection));
10460   plex->anchorSection = anchorSection;
10461 
10462   PetscCall(PetscObjectReference((PetscObject)anchorIS));
10463   PetscCall(ISDestroy(&plex->anchorIS));
10464   plex->anchorIS = anchorIS;
10465 
10466   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
10467     PetscInt        size, a, pStart, pEnd;
10468     const PetscInt *anchors;
10469 
10470     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10471     PetscCall(ISGetLocalSize(anchorIS, &size));
10472     PetscCall(ISGetIndices(anchorIS, &anchors));
10473     for (a = 0; a < size; a++) {
10474       PetscInt p;
10475 
10476       p = anchors[a];
10477       if (p >= pStart && p < pEnd) {
10478         PetscInt dof;
10479 
10480         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10481         if (dof) {
10482           PetscCall(ISRestoreIndices(anchorIS, &anchors));
10483           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
10484         }
10485       }
10486     }
10487     PetscCall(ISRestoreIndices(anchorIS, &anchors));
10488   }
10489   /* reset the generic constraints */
10490   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
10491   PetscFunctionReturn(PETSC_SUCCESS);
10492 }
10493 
10494 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
10495 {
10496   PetscSection anchorSection;
10497   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
10498 
10499   PetscFunctionBegin;
10500   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10501   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10502   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
10503   PetscCall(PetscSectionGetNumFields(section, &numFields));
10504   if (numFields) {
10505     PetscInt f;
10506     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
10507 
10508     for (f = 0; f < numFields; f++) {
10509       PetscInt numComp;
10510 
10511       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
10512       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
10513     }
10514   }
10515   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10516   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10517   pStart = PetscMax(pStart, sStart);
10518   pEnd   = PetscMin(pEnd, sEnd);
10519   pEnd   = PetscMax(pStart, pEnd);
10520   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
10521   for (p = pStart; p < pEnd; p++) {
10522     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10523     if (dof) {
10524       PetscCall(PetscSectionGetDof(section, p, &dof));
10525       PetscCall(PetscSectionSetDof(*cSec, p, dof));
10526       for (f = 0; f < numFields; f++) {
10527         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
10528         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
10529       }
10530     }
10531   }
10532   PetscCall(PetscSectionSetUp(*cSec));
10533   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
10534   PetscFunctionReturn(PETSC_SUCCESS);
10535 }
10536 
10537 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
10538 {
10539   PetscSection    aSec;
10540   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
10541   const PetscInt *anchors;
10542   PetscInt        numFields, f;
10543   IS              aIS;
10544   MatType         mtype;
10545   PetscBool       iscuda, iskokkos;
10546 
10547   PetscFunctionBegin;
10548   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10549   PetscCall(PetscSectionGetStorageSize(cSec, &m));
10550   PetscCall(PetscSectionGetStorageSize(section, &n));
10551   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
10552   PetscCall(MatSetSizes(*cMat, m, n, m, n));
10553   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
10554   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
10555   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
10556   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
10557   if (iscuda) mtype = MATSEQAIJCUSPARSE;
10558   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
10559   else mtype = MATSEQAIJ;
10560   PetscCall(MatSetType(*cMat, mtype));
10561   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
10562   PetscCall(ISGetIndices(aIS, &anchors));
10563   /* cSec will be a subset of aSec and section */
10564   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
10565   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10566   PetscCall(PetscMalloc1(m + 1, &i));
10567   i[0] = 0;
10568   PetscCall(PetscSectionGetNumFields(section, &numFields));
10569   for (p = pStart; p < pEnd; p++) {
10570     PetscInt rDof, rOff, r;
10571 
10572     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10573     if (!rDof) continue;
10574     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10575     if (numFields) {
10576       for (f = 0; f < numFields; f++) {
10577         annz = 0;
10578         for (r = 0; r < rDof; r++) {
10579           a = anchors[rOff + r];
10580           if (a < sStart || a >= sEnd) continue;
10581           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10582           annz += aDof;
10583         }
10584         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10585         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10586         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10587       }
10588     } else {
10589       annz = 0;
10590       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10591       for (q = 0; q < dof; q++) {
10592         a = anchors[rOff + q];
10593         if (a < sStart || a >= sEnd) continue;
10594         PetscCall(PetscSectionGetDof(section, a, &aDof));
10595         annz += aDof;
10596       }
10597       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10598       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10599       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10600     }
10601   }
10602   nnz = i[m];
10603   PetscCall(PetscMalloc1(nnz, &j));
10604   offset = 0;
10605   for (p = pStart; p < pEnd; p++) {
10606     if (numFields) {
10607       for (f = 0; f < numFields; f++) {
10608         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10609         for (q = 0; q < dof; q++) {
10610           PetscInt rDof, rOff, r;
10611           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10612           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10613           for (r = 0; r < rDof; r++) {
10614             PetscInt s;
10615 
10616             a = anchors[rOff + r];
10617             if (a < sStart || a >= sEnd) continue;
10618             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10619             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10620             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10621           }
10622         }
10623       }
10624     } else {
10625       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10626       for (q = 0; q < dof; q++) {
10627         PetscInt rDof, rOff, r;
10628         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10629         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10630         for (r = 0; r < rDof; r++) {
10631           PetscInt s;
10632 
10633           a = anchors[rOff + r];
10634           if (a < sStart || a >= sEnd) continue;
10635           PetscCall(PetscSectionGetDof(section, a, &aDof));
10636           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10637           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10638         }
10639       }
10640     }
10641   }
10642   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10643   PetscCall(PetscFree(i));
10644   PetscCall(PetscFree(j));
10645   PetscCall(ISRestoreIndices(aIS, &anchors));
10646   PetscFunctionReturn(PETSC_SUCCESS);
10647 }
10648 
10649 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10650 {
10651   DM_Plex     *plex = (DM_Plex *)dm->data;
10652   PetscSection anchorSection, section, cSec;
10653   Mat          cMat;
10654 
10655   PetscFunctionBegin;
10656   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10657   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10658   if (anchorSection) {
10659     PetscInt Nf;
10660 
10661     PetscCall(DMGetLocalSection(dm, &section));
10662     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10663     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10664     PetscCall(DMGetNumFields(dm, &Nf));
10665     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10666     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10667     PetscCall(PetscSectionDestroy(&cSec));
10668     PetscCall(MatDestroy(&cMat));
10669   }
10670   PetscFunctionReturn(PETSC_SUCCESS);
10671 }
10672 
10673 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10674 {
10675   IS           subis;
10676   PetscSection section, subsection;
10677 
10678   PetscFunctionBegin;
10679   PetscCall(DMGetLocalSection(dm, &section));
10680   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10681   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10682   /* Create subdomain */
10683   PetscCall(DMPlexFilter(dm, label, value, PETSC_FALSE, PETSC_FALSE, NULL, subdm));
10684   /* Create submodel */
10685   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10686   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10687   PetscCall(DMSetLocalSection(*subdm, subsection));
10688   PetscCall(PetscSectionDestroy(&subsection));
10689   PetscCall(DMCopyDisc(dm, *subdm));
10690   /* Create map from submodel to global model */
10691   if (is) {
10692     PetscSection    sectionGlobal, subsectionGlobal;
10693     IS              spIS;
10694     const PetscInt *spmap;
10695     PetscInt       *subIndices;
10696     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10697     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10698 
10699     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10700     PetscCall(ISGetIndices(spIS, &spmap));
10701     PetscCall(PetscSectionGetNumFields(section, &Nf));
10702     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10703     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10704     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10705     for (p = pStart; p < pEnd; ++p) {
10706       PetscInt gdof, pSubSize = 0;
10707 
10708       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10709       if (gdof > 0) {
10710         for (f = 0; f < Nf; ++f) {
10711           PetscInt fdof, fcdof;
10712 
10713           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10714           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10715           pSubSize += fdof - fcdof;
10716         }
10717         subSize += pSubSize;
10718         if (pSubSize) {
10719           if (bs < 0) {
10720             bs = pSubSize;
10721           } else if (bs != pSubSize) {
10722             /* Layout does not admit a pointwise block size */
10723             bs = 1;
10724           }
10725         }
10726       }
10727     }
10728     /* Must have same blocksize on all procs (some might have no points) */
10729     bsLocal[0] = bs < 0 ? PETSC_INT_MAX : bs;
10730     bsLocal[1] = bs;
10731     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10732     if (bsMinMax[0] != bsMinMax[1]) {
10733       bs = 1;
10734     } else {
10735       bs = bsMinMax[0];
10736     }
10737     PetscCall(PetscMalloc1(subSize, &subIndices));
10738     for (p = pStart; p < pEnd; ++p) {
10739       PetscInt gdof, goff;
10740 
10741       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10742       if (gdof > 0) {
10743         const PetscInt point = spmap[p];
10744 
10745         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10746         for (f = 0; f < Nf; ++f) {
10747           PetscInt fdof, fcdof, fc, f2, poff = 0;
10748 
10749           /* Can get rid of this loop by storing field information in the global section */
10750           for (f2 = 0; f2 < f; ++f2) {
10751             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10752             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10753             poff += fdof - fcdof;
10754           }
10755           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10756           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10757           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10758         }
10759       }
10760     }
10761     PetscCall(ISRestoreIndices(spIS, &spmap));
10762     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10763     if (bs > 1) {
10764       /* We need to check that the block size does not come from non-contiguous fields */
10765       PetscInt i, j, set = 1;
10766       for (i = 0; i < subSize; i += bs) {
10767         for (j = 0; j < bs; ++j) {
10768           if (subIndices[i + j] != subIndices[i] + j) {
10769             set = 0;
10770             break;
10771           }
10772         }
10773       }
10774       if (set) PetscCall(ISSetBlockSize(*is, bs));
10775     }
10776     /* Attach nullspace */
10777     for (f = 0; f < Nf; ++f) {
10778       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10779       if ((*subdm)->nullspaceConstructors[f]) break;
10780     }
10781     if (f < Nf) {
10782       MatNullSpace nullSpace;
10783       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10784 
10785       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10786       PetscCall(MatNullSpaceDestroy(&nullSpace));
10787     }
10788   }
10789   PetscFunctionReturn(PETSC_SUCCESS);
10790 }
10791 
10792 /*@
10793   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10794 
10795   Input Parameters:
10796 + dm    - The `DM`
10797 - dummy - unused argument
10798 
10799   Options Database Key:
10800 . -dm_plex_monitor_throughput - Activate the monitor
10801 
10802   Level: developer
10803 
10804 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10805 @*/
10806 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10807 {
10808   PetscLogHandler default_handler;
10809 
10810   PetscFunctionBegin;
10811   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10812   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10813   if (default_handler) {
10814     PetscLogEvent      event;
10815     PetscEventPerfInfo eventInfo;
10816     PetscLogDouble     cellRate, flopRate;
10817     PetscInt           cStart, cEnd, Nf, N;
10818     const char        *name;
10819 
10820     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10821     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10822     PetscCall(DMGetNumFields(dm, &Nf));
10823     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10824     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10825     N        = (cEnd - cStart) * Nf * eventInfo.count;
10826     flopRate = eventInfo.flops / eventInfo.time;
10827     cellRate = N / eventInfo.time;
10828     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));
10829   } else {
10830     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.");
10831   }
10832   PetscFunctionReturn(PETSC_SUCCESS);
10833 }
10834