xref: /petsc/src/dm/impls/plex/plex.c (revision 55f9ef1db8644e252da9695ccd9136140b7df789)
1 #include <petsc/private/dmpleximpl.h> /*I      "petscdmplex.h"   I*/
2 #include <petsc/private/dmlabelimpl.h>
3 #include <petsc/private/isimpl.h>
4 #include <petsc/private/vecimpl.h>
5 #include <petsc/private/glvisvecimpl.h>
6 #include <petscsf.h>
7 #include <petscds.h>
8 #include <petscdraw.h>
9 #include <petscdmfield.h>
10 #include <petscdmplextransform.h>
11 #include <petscblaslapack.h>
12 
13 /* Logging support */
14 PetscLogEvent DMPLEX_Interpolate, DMPLEX_Partition, DMPLEX_Distribute, DMPLEX_DistributeCones, DMPLEX_DistributeLabels, DMPLEX_DistributeSF, DMPLEX_DistributeOverlap, DMPLEX_DistributeField, DMPLEX_DistributeData, DMPLEX_Migrate, DMPLEX_InterpolateSF, DMPLEX_GlobalToNaturalBegin, DMPLEX_GlobalToNaturalEnd, DMPLEX_NaturalToGlobalBegin, DMPLEX_NaturalToGlobalEnd, DMPLEX_Stratify, DMPLEX_Symmetrize, DMPLEX_Preallocate, DMPLEX_ResidualFEM, DMPLEX_JacobianFEM, DMPLEX_InterpolatorFEM, DMPLEX_InjectorFEM, DMPLEX_IntegralFEM, DMPLEX_CreateGmsh, DMPLEX_CreateBoxSFC, DMPLEX_RebalanceSharedPoints, DMPLEX_PartSelf, DMPLEX_PartLabelInvert, DMPLEX_PartLabelCreateSF, DMPLEX_PartStratSF, DMPLEX_CreatePointSF, DMPLEX_LocatePoints, DMPLEX_TopologyView, DMPLEX_LabelsView, DMPLEX_CoordinatesView, DMPLEX_SectionView, DMPLEX_GlobalVectorView, DMPLEX_LocalVectorView, DMPLEX_TopologyLoad, DMPLEX_LabelsLoad, DMPLEX_CoordinatesLoad, DMPLEX_SectionLoad, DMPLEX_GlobalVectorLoad, DMPLEX_LocalVectorLoad;
15 PetscLogEvent DMPLEX_RebalBuildGraph, DMPLEX_RebalRewriteSF, DMPLEX_RebalGatherGraph, DMPLEX_RebalPartition, DMPLEX_RebalScatterPart, DMPLEX_Generate, DMPLEX_Transform, DMPLEX_GetLocalOffsets, DMPLEX_Uninterpolate;
16 
17 /* Logging support */
18 PetscLogEvent DMPLEX_DistributionView, DMPLEX_DistributionLoad;
19 
20 PetscBool  Plexcite       = PETSC_FALSE;
21 const char PlexCitation[] = "@article{LangeMitchellKnepleyGorman2015,\n"
22                             "title     = {Efficient mesh management in {Firedrake} using {PETSc-DMPlex}},\n"
23                             "author    = {Michael Lange and Lawrence Mitchell and Matthew G. Knepley and Gerard J. Gorman},\n"
24                             "journal   = {SIAM Journal on Scientific Computing},\n"
25                             "volume    = {38},\n"
26                             "number    = {5},\n"
27                             "pages     = {S143--S155},\n"
28                             "eprint    = {http://arxiv.org/abs/1506.07749},\n"
29                             "doi       = {10.1137/15M1026092},\n"
30                             "year      = {2016},\n"
31                             "petsc_uses={DMPlex},\n}\n";
32 
33 PETSC_SINGLE_LIBRARY_INTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
34 
35 /*@
36   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
37 
38   Input Parameter:
39 . dm - The `DMPLEX` object
40 
41   Output Parameter:
42 . simplex - Flag checking for a simplex
43 
44   Level: intermediate
45 
46   Note:
47   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
48   If the mesh has no cells, this returns `PETSC_FALSE`.
49 
50 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
51 @*/
52 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
53 {
54   DMPolytopeType ct;
55   PetscInt       cStart, cEnd;
56 
57   PetscFunctionBegin;
58   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
59   if (cEnd <= cStart) {
60     *simplex = PETSC_FALSE;
61     PetscFunctionReturn(PETSC_SUCCESS);
62   }
63   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
64   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
65   PetscFunctionReturn(PETSC_SUCCESS);
66 }
67 
68 /*@
69   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
70 
71   Input Parameters:
72 + dm     - The `DMPLEX` object
73 - height - The cell height in the Plex, 0 is the default
74 
75   Output Parameters:
76 + cStart - The first "normal" cell, pass `NULL` if not needed
77 - cEnd   - The upper bound on "normal" cells, pass `NULL` if not needed
78 
79   Level: developer
80 
81   Note:
82   This function requires that tensor cells are ordered last.
83 
84 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetCellTypeStratum()`
85 @*/
86 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PeOp PetscInt *cStart, PeOp PetscInt *cEnd)
87 {
88   DMLabel         ctLabel;
89   IS              valueIS;
90   const PetscInt *ctypes;
91   PetscBool       found = PETSC_FALSE;
92   PetscInt        Nct, cS = PETSC_INT_MAX, cE = 0;
93 
94   PetscFunctionBegin;
95   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
96   PetscCall(DMLabelGetValueIS(ctLabel, &valueIS));
97   PetscCall(ISGetLocalSize(valueIS, &Nct));
98   PetscCall(ISGetIndices(valueIS, &ctypes));
99   for (PetscInt t = 0; t < Nct; ++t) {
100     const DMPolytopeType ct = (DMPolytopeType)ctypes[t];
101     PetscInt             ctS, ctE, ht;
102 
103     if (ct == DM_POLYTOPE_UNKNOWN) {
104       // If any cells are not typed, just use all cells
105       PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), cStart, cEnd));
106       break;
107     }
108     if (DMPolytopeTypeIsHybrid(ct) || ct == DM_POLYTOPE_FV_GHOST) continue;
109     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &ctS, &ctE));
110     if (ctS >= ctE) continue;
111     // Check that a point has the right height
112     PetscCall(DMPlexGetPointHeight(dm, ctS, &ht));
113     if (ht != height) continue;
114     cS    = PetscMin(cS, ctS);
115     cE    = PetscMax(cE, ctE);
116     found = PETSC_TRUE;
117   }
118   if (!Nct || !found) cS = cE = 0;
119   PetscCall(ISDestroy(&valueIS));
120   // Reset label for fast lookup
121   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
122   if (cStart) *cStart = cS;
123   if (cEnd) *cEnd = cE;
124   PetscFunctionReturn(PETSC_SUCCESS);
125 }
126 
127 PetscErrorCode DMPlexGetFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **ssStart, PetscInt **ssEnd, PetscViewerVTKFieldType **sft)
128 {
129   PetscInt                 cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd, c, depth, cellHeight, t;
130   PetscInt                *sStart, *sEnd;
131   PetscViewerVTKFieldType *ft;
132   PetscInt                 vcdof[DM_NUM_POLYTOPES + 1], globalvcdof[DM_NUM_POLYTOPES + 1];
133   DMLabel                  depthLabel, ctLabel;
134 
135   PetscFunctionBegin;
136   /* the vcdof and globalvcdof are sized to allow every polytope type and simple vertex at DM_NUM_POLYTOPES */
137   PetscCall(PetscArrayzero(vcdof, DM_NUM_POLYTOPES + 1));
138   PetscCall(DMGetCoordinateDim(dm, &cdim));
139   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
140   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
141   if (field >= 0) {
142     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[DM_NUM_POLYTOPES]));
143   } else {
144     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[DM_NUM_POLYTOPES]));
145   }
146 
147   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
148   PetscCall(DMPlexGetDepth(dm, &depth));
149   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
150   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
151   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
152     const DMPolytopeType ict = (DMPolytopeType)c;
153     PetscInt             dep;
154 
155     if (ict == DM_POLYTOPE_FV_GHOST) continue;
156     PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
157     if (pStart >= 0) {
158       PetscCall(DMLabelGetValue(depthLabel, cStart, &dep));
159       if (dep != depth - cellHeight) continue;
160     }
161     if (field >= 0) {
162       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[c]));
163     } else {
164       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[c]));
165     }
166   }
167 
168   PetscCallMPI(MPIU_Allreduce(vcdof, globalvcdof, DM_NUM_POLYTOPES + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
169   *types = 0;
170 
171   for (c = 0; c < DM_NUM_POLYTOPES + 1; ++c) {
172     if (globalvcdof[c]) ++(*types);
173   }
174 
175   PetscCall(PetscMalloc3(*types, &sStart, *types, &sEnd, *types, &ft));
176   t = 0;
177   if (globalvcdof[DM_NUM_POLYTOPES]) {
178     sStart[t] = vStart;
179     sEnd[t]   = vEnd;
180     ft[t]     = (globalvcdof[t] == cdim) ? PETSC_VTK_POINT_VECTOR_FIELD : PETSC_VTK_POINT_FIELD;
181     ++t;
182   }
183 
184   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
185     if (globalvcdof[c]) {
186       const DMPolytopeType ict = (DMPolytopeType)c;
187 
188       PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
189       sStart[t] = cStart;
190       sEnd[t]   = cEnd;
191       ft[t]     = (globalvcdof[c] == cdim) ? PETSC_VTK_CELL_VECTOR_FIELD : PETSC_VTK_CELL_FIELD;
192       ++t;
193     }
194   }
195 
196   if (!*types) {
197     if (field >= 0) {
198       const char *fieldname;
199 
200       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
201       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
202     } else {
203       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
204     }
205   }
206 
207   *ssStart = sStart;
208   *ssEnd   = sEnd;
209   *sft     = ft;
210   PetscFunctionReturn(PETSC_SUCCESS);
211 }
212 
213 PetscErrorCode DMPlexRestoreFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **sStart, PetscInt **sEnd, PetscViewerVTKFieldType **ft)
214 {
215   PetscFunctionBegin;
216   PetscCall(PetscFree3(*sStart, *sEnd, *ft));
217   PetscFunctionReturn(PETSC_SUCCESS);
218 }
219 
220 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
221 {
222   PetscInt cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
223   PetscInt vcdof[2] = {0, 0}, globalvcdof[2];
224 
225   PetscFunctionBegin;
226   *ft = PETSC_VTK_INVALID;
227   PetscCall(DMGetCoordinateDim(dm, &cdim));
228   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
229   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
230   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
231   if (field >= 0) {
232     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
233     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
234   } else {
235     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
236     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
237   }
238   PetscCallMPI(MPIU_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
239   if (globalvcdof[0]) {
240     *sStart = vStart;
241     *sEnd   = vEnd;
242     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
243     else *ft = PETSC_VTK_POINT_FIELD;
244   } else if (globalvcdof[1]) {
245     *sStart = cStart;
246     *sEnd   = cEnd;
247     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
248     else *ft = PETSC_VTK_CELL_FIELD;
249   } else {
250     if (field >= 0) {
251       const char *fieldname;
252 
253       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
254       PetscCall(PetscInfo(dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
255     } else {
256       PetscCall(PetscInfo(dm, "Could not classify VTK output type of section\n"));
257     }
258   }
259   PetscFunctionReturn(PETSC_SUCCESS);
260 }
261 
262 /*@
263   DMPlexVecView1D - Plot many 1D solutions on the same line graph
264 
265   Collective
266 
267   Input Parameters:
268 + dm     - The `DMPLEX` object
269 . n      - The number of vectors
270 . u      - The array of local vectors
271 - viewer - The `PetscViewer`
272 
273   Level: advanced
274 
275 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `VecViewFromOptions()`, `VecView()`
276 @*/
277 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
278 {
279   DM                 cdm;
280   PetscDS            ds;
281   PetscDraw          draw = NULL;
282   PetscDrawLG        lg;
283   Vec                coordinates;
284   const PetscScalar *coords, **sol;
285   PetscReal         *vals;
286   PetscInt          *Nc;
287   PetscInt           Nf, Nl, vStart, vEnd, eStart, eEnd;
288   char             **names;
289 
290   PetscFunctionBegin;
291   PetscCall(DMGetCoordinateDM(dm, &cdm));
292   PetscCall(DMGetDS(dm, &ds));
293   PetscCall(PetscDSGetNumFields(ds, &Nf));
294   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
295   PetscCall(PetscDSGetComponents(ds, &Nc));
296 
297   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
298   if (!draw) PetscFunctionReturn(PETSC_SUCCESS);
299   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
300 
301   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
302   for (PetscInt i = 0, l = 0; i < n; ++i) {
303     const char *vname;
304 
305     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
306     for (PetscInt f = 0; f < Nf; ++f) {
307       PetscObject disc;
308       const char *fname;
309       char        tmpname[PETSC_MAX_PATH_LEN];
310 
311       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
312       /* TODO Create names for components */
313       for (PetscInt c = 0; c < Nc[f]; ++c, ++l) {
314         PetscCall(PetscObjectGetName(disc, &fname));
315         PetscCall(PetscStrncpy(tmpname, vname, sizeof(tmpname)));
316         PetscCall(PetscStrlcat(tmpname, ":", sizeof(tmpname)));
317         PetscCall(PetscStrlcat(tmpname, fname, sizeof(tmpname)));
318         PetscCall(PetscStrallocpy(tmpname, &names[l]));
319       }
320     }
321   }
322   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
323   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
324   PetscCall(VecGetArrayRead(coordinates, &coords));
325   for (PetscInt i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
326   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
327   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
328   PetscSection s;
329   PetscInt     cdof, vdof;
330 
331   PetscCall(DMGetLocalSection(dm, &s));
332   PetscCall(PetscSectionGetDof(s, eStart, &cdof));
333   PetscCall(PetscSectionGetDof(s, vStart, &vdof));
334   if (cdof) {
335     if (vdof) {
336       // P_2
337       PetscInt vFirst = -1;
338 
339       for (PetscInt e = eStart; e < eEnd; ++e) {
340         PetscScalar    *xa, *xb, *svals;
341         const PetscInt *cone;
342 
343         PetscCall(DMPlexGetCone(dm, e, &cone));
344         PetscCall(DMPlexPointLocalRead(cdm, cone[0], coords, &xa));
345         PetscCall(DMPlexPointLocalRead(cdm, cone[1], coords, &xb));
346         if (e == eStart) vFirst = cone[0];
347         for (PetscInt i = 0; i < n; ++i) {
348           PetscCall(DMPlexPointLocalRead(dm, cone[0], sol[i], &svals));
349           for (PetscInt l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
350         }
351         PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(xa[0]), vals));
352         if (e == eEnd - 1 && cone[1] != vFirst) {
353           for (PetscInt i = 0; i < n; ++i) {
354             PetscCall(DMPlexPointLocalRead(dm, e, sol[i], &svals));
355             for (PetscInt l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
356           }
357           PetscCall(PetscDrawLGAddCommonPoint(lg, 0.5 * (PetscRealPart(xa[0]) + PetscRealPart(xb[0])), vals));
358           for (PetscInt i = 0; i < n; ++i) {
359             PetscCall(DMPlexPointLocalRead(dm, cone[1], sol[i], &svals));
360             for (PetscInt l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
361           }
362           PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(xb[0]), vals));
363         }
364       }
365     } else {
366       // P_0
367       for (PetscInt e = eStart; e < eEnd; ++e) {
368         PetscScalar    *xa, *xb, *svals;
369         const PetscInt *cone;
370 
371         PetscCall(DMPlexGetCone(dm, e, &cone));
372         PetscCall(DMPlexPointLocalRead(cdm, cone[0], coords, &xa));
373         PetscCall(DMPlexPointLocalRead(cdm, cone[1], coords, &xb));
374         for (PetscInt i = 0; i < n; ++i) {
375           PetscCall(DMPlexPointLocalRead(dm, e, sol[i], &svals));
376           for (PetscInt l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
377         }
378         PetscCall(PetscDrawLGAddCommonPoint(lg, 0.5 * (PetscRealPart(xa[0]) + PetscRealPart(xb[0])), vals));
379       }
380     }
381   } else if (vdof) {
382     // P_1
383     for (PetscInt v = vStart; v < vEnd; ++v) {
384       PetscScalar *x, *svals;
385 
386       PetscCall(DMPlexPointLocalRead(cdm, v, coords, &x));
387       for (PetscInt i = 0; i < n; ++i) {
388         PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
389         for (PetscInt l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
390       }
391       PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
392     }
393   } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Discretization not supported");
394   PetscCall(VecRestoreArrayRead(coordinates, &coords));
395   for (PetscInt i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
396   for (PetscInt l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
397   PetscCall(PetscFree3(sol, names, vals));
398 
399   PetscCall(PetscDrawLGDraw(lg));
400   PetscCall(PetscDrawLGDestroy(&lg));
401   PetscFunctionReturn(PETSC_SUCCESS);
402 }
403 
404 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
405 {
406   DM dm;
407 
408   PetscFunctionBegin;
409   PetscCall(VecGetDM(u, &dm));
410   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
411   PetscFunctionReturn(PETSC_SUCCESS);
412 }
413 
414 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
415 {
416   DM                 dm;
417   PetscSection       s;
418   PetscDraw          draw, popup;
419   DM                 cdm;
420   PetscSection       coordSection;
421   Vec                coordinates;
422   const PetscScalar *array;
423   PetscReal          lbound[3], ubound[3];
424   PetscReal          vbound[2], time;
425   PetscBool          flg;
426   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
427   const char        *name;
428   char               title[PETSC_MAX_PATH_LEN];
429 
430   PetscFunctionBegin;
431   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
432   PetscCall(VecGetDM(v, &dm));
433   PetscCall(DMGetCoordinateDim(dm, &dim));
434   PetscCall(DMGetLocalSection(dm, &s));
435   PetscCall(PetscSectionGetNumFields(s, &Nf));
436   PetscCall(DMGetCoarsenLevel(dm, &level));
437   PetscCall(DMGetCoordinateDM(dm, &cdm));
438   PetscCall(DMGetLocalSection(cdm, &coordSection));
439   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
440   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
441   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
442 
443   PetscCall(PetscObjectGetName((PetscObject)v, &name));
444   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
445 
446   PetscCall(VecGetLocalSize(coordinates, &N));
447   PetscCall(DMGetBoundingBox(dm, lbound, ubound));
448   PetscCall(PetscDrawClear(draw));
449 
450   /* Could implement something like DMDASelectFields() */
451   for (f = 0; f < Nf; ++f) {
452     DM          fdm = dm;
453     Vec         fv  = v;
454     IS          fis;
455     char        prefix[PETSC_MAX_PATH_LEN];
456     const char *fname;
457 
458     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
459     PetscCall(PetscSectionGetFieldName(s, f, &fname));
460 
461     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
462     else prefix[0] = '\0';
463     if (Nf > 1) {
464       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
465       PetscCall(VecGetSubVector(v, fis, &fv));
466       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
467       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
468     }
469     for (comp = 0; comp < Nc; ++comp, ++w) {
470       PetscInt nmax = 2;
471 
472       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
473       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
474       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
475       PetscCall(PetscDrawSetTitle(draw, title));
476 
477       /* TODO Get max and min only for this component */
478       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
479       if (!flg) {
480         PetscCall(VecMin(fv, NULL, &vbound[0]));
481         PetscCall(VecMax(fv, NULL, &vbound[1]));
482         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
483       }
484 
485       PetscCall(PetscDrawGetPopup(draw, &popup));
486       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
487       PetscCall(PetscDrawSetCoordinates(draw, lbound[0], lbound[1], ubound[0], ubound[1]));
488       PetscCall(VecGetArrayRead(fv, &array));
489       for (c = cStart; c < cEnd; ++c) {
490         DMPolytopeType     ct;
491         PetscScalar       *coords = NULL, *a = NULL;
492         const PetscScalar *coords_arr;
493         PetscBool          isDG;
494         PetscInt           numCoords;
495         int                color[4] = {-1, -1, -1, -1};
496 
497         PetscCall(DMPlexGetCellType(dm, c, &ct));
498         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
499         if (a) {
500           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
501           color[1] = color[2] = color[3] = color[0];
502         } else {
503           PetscScalar *vals = NULL;
504           PetscInt     numVals, va;
505 
506           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
507           if (!numVals) {
508             PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
509             continue;
510           }
511           PetscCheck(numVals % Nc == 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "The number of components %" PetscInt_FMT " does not divide the number of values in the closure %" PetscInt_FMT, Nc, numVals);
512           switch (numVals / Nc) {
513           case 1: /* P1 Clamped Segment Prism */
514           case 2: /* P1 Segment Prism, P2 Clamped Segment Prism */
515             PetscCheck(ct == DM_POLYTOPE_SEG_PRISM_TENSOR, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell should be a tensor segment, but it is a %s", DMPolytopeTypes[ct]);
516             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
517             break;
518           case 3: /* P1 Triangle */
519           case 4: /* P1 Quadrangle */
520             PetscCheck(ct == DM_POLYTOPE_TRIANGLE || ct == DM_POLYTOPE_QUADRILATERAL || ct == DM_POLYTOPE_SEG_PRISM_TENSOR, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell should be a triangle or quad, but it is a %s", DMPolytopeTypes[ct]);
521             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
522             break;
523           case 6: /* P2 Triangle */
524           case 8: /* P2 Quadrangle */
525             PetscCheck(ct == DM_POLYTOPE_TRIANGLE || ct == DM_POLYTOPE_QUADRILATERAL || ct == DM_POLYTOPE_SEG_PRISM_TENSOR, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell should be a triangle or quad, but it is a %s", DMPolytopeTypes[ct]);
526             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
527             break;
528           default:
529             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
530           }
531           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
532         }
533         PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
534         switch (numCoords) {
535         case 6:
536         case 12: /* Localized triangle */
537           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
538           break;
539         case 8:
540         case 16: /* Localized quadrilateral */
541           if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR) {
542             PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscMax(color[0], color[1])));
543           } else {
544             PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
545             PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), color[2], color[3], color[0]));
546           }
547           break;
548         default:
549           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
550         }
551         PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
552       }
553       PetscCall(VecRestoreArrayRead(fv, &array));
554       PetscCall(PetscDrawFlush(draw));
555       PetscCall(PetscDrawPause(draw));
556       PetscCall(PetscDrawSave(draw));
557     }
558     if (Nf > 1) {
559       PetscCall(VecRestoreSubVector(v, fis, &fv));
560       PetscCall(ISDestroy(&fis));
561       PetscCall(DMDestroy(&fdm));
562     }
563   }
564   PetscFunctionReturn(PETSC_SUCCESS);
565 }
566 
567 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
568 {
569   DM        dm;
570   PetscDraw draw;
571   PetscInt  dim;
572   PetscBool isnull;
573 
574   PetscFunctionBegin;
575   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
576   PetscCall(PetscDrawIsNull(draw, &isnull));
577   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
578 
579   PetscCall(VecGetDM(v, &dm));
580   PetscCall(DMGetCoordinateDim(dm, &dim));
581   switch (dim) {
582   case 1:
583     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
584     break;
585   case 2:
586     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
587     break;
588   default:
589     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
590   }
591   PetscFunctionReturn(PETSC_SUCCESS);
592 }
593 
594 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
595 {
596   DM                      dm;
597   Vec                     locv;
598   const char             *name;
599   PetscSection            section;
600   PetscInt                pStart, pEnd;
601   PetscInt                numFields;
602   PetscViewerVTKFieldType ft;
603 
604   PetscFunctionBegin;
605   PetscCall(VecGetDM(v, &dm));
606   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
607   PetscCall(PetscObjectGetName((PetscObject)v, &name));
608   PetscCall(PetscObjectSetName((PetscObject)locv, name));
609   PetscCall(VecCopy(v, locv));
610   PetscCall(DMGetLocalSection(dm, &section));
611   PetscCall(PetscSectionGetNumFields(section, &numFields));
612   if (!numFields) {
613     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
614     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
615   } else {
616     PetscInt f;
617 
618     for (f = 0; f < numFields; f++) {
619       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
620       if (ft == PETSC_VTK_INVALID) continue;
621       PetscCall(PetscObjectReference((PetscObject)locv));
622       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
623     }
624     PetscCall(VecDestroy(&locv));
625   }
626   PetscFunctionReturn(PETSC_SUCCESS);
627 }
628 
629 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
630 {
631   DM        dm;
632   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns, ispython;
633 
634   PetscFunctionBegin;
635   PetscCall(VecGetDM(v, &dm));
636   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
637   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
638   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
639   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
640   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
641   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
642   PetscCall(PetscObjectHasFunction((PetscObject)viewer, "PetscViewerPythonViewObject_C", &ispython));
643   if (isvtk || ishdf5 || isdraw || isglvis || iscgns || ispython) {
644     PetscInt    i, numFields;
645     PetscObject fe;
646     PetscBool   fem  = PETSC_FALSE;
647     Vec         locv = v;
648     const char *name;
649     PetscInt    step;
650     PetscReal   time;
651 
652     PetscCall(DMGetNumFields(dm, &numFields));
653     for (i = 0; i < numFields; i++) {
654       PetscCall(DMGetField(dm, i, NULL, &fe));
655       if (fe->classid == PETSCFE_CLASSID) {
656         fem = PETSC_TRUE;
657         break;
658       }
659     }
660     if (fem) {
661       PetscObject isZero;
662 
663       PetscCall(DMGetLocalVector(dm, &locv));
664       PetscCall(PetscObjectGetName((PetscObject)v, &name));
665       PetscCall(PetscObjectSetName((PetscObject)locv, name));
666       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
667       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
668       PetscCall(VecCopy(v, locv));
669       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
670       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
671     }
672     if (isvtk) {
673       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
674     } else if (ishdf5) {
675 #if defined(PETSC_HAVE_HDF5)
676       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
677 #else
678       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
679 #endif
680     } else if (isdraw) {
681       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
682     } else if (ispython) {
683       PetscCall(PetscViewerPythonViewObject(viewer, (PetscObject)locv));
684     } else if (isglvis) {
685       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
686       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
687       PetscCall(VecView_GLVis(locv, viewer));
688     } else if (iscgns) {
689 #if defined(PETSC_HAVE_CGNS)
690       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
691 #else
692       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
693 #endif
694     }
695     if (fem) {
696       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
697       PetscCall(DMRestoreLocalVector(dm, &locv));
698     }
699   } else {
700     PetscBool isseq;
701 
702     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
703     if (isseq) PetscCall(VecView_Seq(v, viewer));
704     else PetscCall(VecView_MPI(v, viewer));
705   }
706   PetscFunctionReturn(PETSC_SUCCESS);
707 }
708 
709 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
710 {
711   DM        dm;
712   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns, ispython;
713 
714   PetscFunctionBegin;
715   PetscCall(VecGetDM(v, &dm));
716   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
717   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
718   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
719   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
720   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
721   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
722   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
723   PetscCall(PetscObjectHasFunction((PetscObject)viewer, "PetscViewerPythonViewObject_C", &ispython));
724   if (isvtk || isdraw || isglvis || iscgns || ispython) {
725     Vec         locv;
726     PetscObject isZero;
727     const char *name;
728 
729     PetscCall(DMGetLocalVector(dm, &locv));
730     PetscCall(PetscObjectGetName((PetscObject)v, &name));
731     PetscCall(PetscObjectSetName((PetscObject)locv, name));
732     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
733     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
734     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
735     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
736     PetscCall(VecView_Plex_Local(locv, viewer));
737     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
738     PetscCall(DMRestoreLocalVector(dm, &locv));
739   } else if (ishdf5) {
740 #if defined(PETSC_HAVE_HDF5)
741     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
742 #else
743     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
744 #endif
745   } else if (isexodusii) {
746 #if defined(PETSC_HAVE_EXODUSII)
747     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
748 #else
749     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
750 #endif
751   } else {
752     PetscBool isseq;
753 
754     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
755     if (isseq) PetscCall(VecView_Seq(v, viewer));
756     else PetscCall(VecView_MPI(v, viewer));
757   }
758   PetscFunctionReturn(PETSC_SUCCESS);
759 }
760 
761 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
762 {
763   DM                dm;
764   MPI_Comm          comm;
765   PetscViewerFormat format;
766   Vec               v;
767   PetscBool         isvtk, ishdf5;
768 
769   PetscFunctionBegin;
770   PetscCall(VecGetDM(originalv, &dm));
771   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
772   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
773   PetscCall(PetscViewerGetFormat(viewer, &format));
774   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
775   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
776   if (format == PETSC_VIEWER_NATIVE) {
777     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
778     /* this need a better fix */
779     if (dm->useNatural) {
780       if (dm->sfNatural) {
781         const char *vecname;
782         PetscInt    n, nroots;
783 
784         PetscCall(VecGetLocalSize(originalv, &n));
785         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
786         if (n == nroots) {
787           PetscCall(DMPlexCreateNaturalVector(dm, &v));
788           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
789           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
790           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
791           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
792         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
793       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
794     } else v = originalv;
795   } else v = originalv;
796 
797   if (ishdf5) {
798 #if defined(PETSC_HAVE_HDF5)
799     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
800 #else
801     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
802 #endif
803   } else if (isvtk) {
804     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
805   } else {
806     PetscBool isseq;
807 
808     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
809     if (isseq) PetscCall(VecView_Seq(v, viewer));
810     else PetscCall(VecView_MPI(v, viewer));
811   }
812   if (v != originalv) PetscCall(VecDestroy(&v));
813   PetscFunctionReturn(PETSC_SUCCESS);
814 }
815 
816 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
817 {
818   DM        dm;
819   PetscBool ishdf5;
820 
821   PetscFunctionBegin;
822   PetscCall(VecGetDM(v, &dm));
823   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
824   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
825   if (ishdf5) {
826     DM          dmBC;
827     Vec         gv;
828     const char *name;
829 
830     PetscCall(DMGetOutputDM(dm, &dmBC));
831     PetscCall(DMGetGlobalVector(dmBC, &gv));
832     PetscCall(PetscObjectGetName((PetscObject)v, &name));
833     PetscCall(PetscObjectSetName((PetscObject)gv, name));
834     PetscCall(VecLoad_Default(gv, viewer));
835     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
836     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
837     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
838   } else PetscCall(VecLoad_Default(v, viewer));
839   PetscFunctionReturn(PETSC_SUCCESS);
840 }
841 
842 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
843 {
844   DM        dm;
845   PetscBool ishdf5, isexodusii, iscgns;
846 
847   PetscFunctionBegin;
848   PetscCall(VecGetDM(v, &dm));
849   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
850   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
851   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
852   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
853   if (ishdf5) {
854 #if defined(PETSC_HAVE_HDF5)
855     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
856 #else
857     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
858 #endif
859   } else if (isexodusii) {
860 #if defined(PETSC_HAVE_EXODUSII)
861     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
862 #else
863     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
864 #endif
865   } else if (iscgns) {
866 #if defined(PETSC_HAVE_CGNS)
867     PetscCall(VecLoad_Plex_CGNS_Internal(v, viewer));
868 #else
869     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
870 #endif
871   } else PetscCall(VecLoad_Default(v, viewer));
872   PetscFunctionReturn(PETSC_SUCCESS);
873 }
874 
875 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
876 {
877   DM                dm;
878   PetscViewerFormat format;
879   PetscBool         ishdf5;
880 
881   PetscFunctionBegin;
882   PetscCall(VecGetDM(originalv, &dm));
883   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
884   PetscCall(PetscViewerGetFormat(viewer, &format));
885   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
886   if (format == PETSC_VIEWER_NATIVE) {
887     if (dm->useNatural) {
888       if (dm->sfNatural) {
889         if (ishdf5) {
890 #if defined(PETSC_HAVE_HDF5)
891           Vec         v;
892           const char *vecname;
893 
894           PetscCall(DMPlexCreateNaturalVector(dm, &v));
895           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
896           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
897           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
898           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
899           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
900           PetscCall(VecDestroy(&v));
901 #else
902           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
903 #endif
904         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
905       }
906     } else PetscCall(VecLoad_Default(originalv, viewer));
907   }
908   PetscFunctionReturn(PETSC_SUCCESS);
909 }
910 
911 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
912 {
913   PetscSection       coordSection;
914   Vec                coordinates;
915   DMLabel            depthLabel, celltypeLabel;
916   const char        *name[4];
917   const PetscScalar *a;
918   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
919 
920   PetscFunctionBegin;
921   PetscCall(DMGetDimension(dm, &dim));
922   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
923   PetscCall(DMGetCoordinateSection(dm, &coordSection));
924   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
925   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
926   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
927   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
928   PetscCall(VecGetArrayRead(coordinates, &a));
929   name[0]       = "vertex";
930   name[1]       = "edge";
931   name[dim - 1] = "face";
932   name[dim]     = "cell";
933   for (c = cStart; c < cEnd; ++c) {
934     PetscInt *closure = NULL;
935     PetscInt  closureSize, cl, ct;
936 
937     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
938     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
939     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
940     PetscCall(PetscViewerASCIIPushTab(viewer));
941     for (cl = 0; cl < closureSize * 2; cl += 2) {
942       PetscInt point = closure[cl], depth, dof, off, d, p;
943 
944       if ((point < pStart) || (point >= pEnd)) continue;
945       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
946       if (!dof) continue;
947       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
948       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
949       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
950       for (p = 0; p < dof / dim; ++p) {
951         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
952         for (d = 0; d < dim; ++d) {
953           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
954           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
955         }
956         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
957       }
958       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
959     }
960     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
961     PetscCall(PetscViewerASCIIPopTab(viewer));
962   }
963   PetscCall(VecRestoreArrayRead(coordinates, &a));
964   PetscFunctionReturn(PETSC_SUCCESS);
965 }
966 
967 typedef enum {
968   CS_CARTESIAN,
969   CS_POLAR,
970   CS_CYLINDRICAL,
971   CS_SPHERICAL
972 } CoordSystem;
973 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
974 
975 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
976 {
977   PetscInt i;
978 
979   PetscFunctionBegin;
980   if (dim > 3) {
981     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
982   } else {
983     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
984 
985     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
986     switch (cs) {
987     case CS_CARTESIAN:
988       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
989       break;
990     case CS_POLAR:
991       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
992       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
993       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
994       break;
995     case CS_CYLINDRICAL:
996       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
997       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
998       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
999       trcoords[2] = coords[2];
1000       break;
1001     case CS_SPHERICAL:
1002       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
1003       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
1004       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
1005       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
1006       break;
1007     }
1008     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
1009   }
1010   PetscFunctionReturn(PETSC_SUCCESS);
1011 }
1012 
1013 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
1014 {
1015   DM_Plex          *mesh = (DM_Plex *)dm->data;
1016   DM                cdm, cdmCell;
1017   PetscSection      coordSection, coordSectionCell;
1018   Vec               coordinates, coordinatesCell;
1019   PetscViewerFormat format;
1020 
1021   PetscFunctionBegin;
1022   PetscCall(PetscViewerGetFormat(viewer, &format));
1023   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
1024     const char *name;
1025     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
1026     PetscInt    pStart, pEnd, p, numLabels, l;
1027     PetscMPIInt rank, size;
1028 
1029     PetscCall(DMGetCoordinateDM(dm, &cdm));
1030     PetscCall(DMGetCoordinateSection(dm, &coordSection));
1031     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1032     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
1033     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
1034     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
1035     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1036     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1037     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1038     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1039     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
1040     PetscCall(DMGetDimension(dm, &dim));
1041     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1042     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1043     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1044     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1045     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
1046     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1047     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
1048     for (p = pStart; p < pEnd; ++p) {
1049       PetscInt dof, off, s;
1050 
1051       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
1052       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
1053       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
1054     }
1055     PetscCall(PetscViewerFlush(viewer));
1056     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
1057     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
1058     for (p = pStart; p < pEnd; ++p) {
1059       PetscInt dof, off, c;
1060 
1061       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
1062       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
1063       for (c = off; c < off + dof; ++c) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " <---- %" PetscInt_FMT " (%" PetscInt_FMT ")\n", rank, p, mesh->cones[c], mesh->coneOrientations[c]));
1064     }
1065     PetscCall(PetscViewerFlush(viewer));
1066     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1067     if (coordSection && coordinates) {
1068       CoordSystem        cs = CS_CARTESIAN;
1069       const PetscScalar *array, *arrayCell = NULL;
1070       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_INT_MAX, pcEnd = PETSC_INT_MIN, pStart, pEnd, p;
1071       PetscMPIInt        rank;
1072       const char        *name;
1073 
1074       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
1075       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
1076       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
1077       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
1078       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
1079       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
1080       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
1081       pStart = PetscMin(pvStart, pcStart);
1082       pEnd   = PetscMax(pvEnd, pcEnd);
1083       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
1084       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
1085       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
1086       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
1087 
1088       PetscCall(VecGetArrayRead(coordinates, &array));
1089       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
1090       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1091       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
1092       for (p = pStart; p < pEnd; ++p) {
1093         PetscInt dof, off;
1094 
1095         if (p >= pvStart && p < pvEnd) {
1096           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
1097           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
1098           if (dof) {
1099             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dof %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1100             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
1101             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1102           }
1103         }
1104         if (cdmCell && p >= pcStart && p < pcEnd) {
1105           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
1106           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
1107           if (dof) {
1108             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dof %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1109             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
1110             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1111           }
1112         }
1113       }
1114       PetscCall(PetscViewerFlush(viewer));
1115       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1116       PetscCall(VecRestoreArrayRead(coordinates, &array));
1117       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
1118     }
1119     PetscCall(DMGetNumLabels(dm, &numLabels));
1120     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1121     for (l = 0; l < numLabels; ++l) {
1122       DMLabel     label;
1123       PetscBool   isdepth;
1124       const char *name;
1125 
1126       PetscCall(DMGetLabelName(dm, l, &name));
1127       PetscCall(PetscStrcmp(name, "depth", &isdepth));
1128       if (isdepth) continue;
1129       PetscCall(DMGetLabel(dm, name, &label));
1130       PetscCall(DMLabelView(label, viewer));
1131     }
1132     if (size > 1) {
1133       PetscSF sf;
1134 
1135       PetscCall(DMGetPointSF(dm, &sf));
1136       PetscCall(PetscSFView(sf, viewer));
1137     }
1138     if (mesh->periodic.face_sfs)
1139       for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFView(mesh->periodic.face_sfs[i], viewer));
1140     PetscCall(PetscViewerFlush(viewer));
1141   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
1142     const char  *name, *color;
1143     const char  *defcolors[3]  = {"gray", "orange", "green"};
1144     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
1145     char         lname[PETSC_MAX_PATH_LEN];
1146     PetscReal    scale      = 2.0;
1147     PetscReal    tikzscale  = 1.0;
1148     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
1149     double       tcoords[3];
1150     PetscScalar *coords;
1151     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, fStart = 0, fEnd = 0, e, p, n;
1152     PetscMPIInt  rank, size;
1153     char       **names, **colors, **lcolors;
1154     PetscBool    flg, lflg;
1155     PetscBT      wp = NULL;
1156     PetscInt     pEnd, pStart;
1157 
1158     PetscCall(DMGetCoordinateDM(dm, &cdm));
1159     PetscCall(DMGetCoordinateSection(dm, &coordSection));
1160     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1161     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
1162     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
1163     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
1164     PetscCall(DMGetDimension(dm, &dim));
1165     PetscCall(DMPlexGetDepth(dm, &depth));
1166     PetscCall(DMGetNumLabels(dm, &numLabels));
1167     numLabels  = PetscMax(numLabels, 10);
1168     numColors  = 10;
1169     numLColors = 10;
1170     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
1171     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
1172     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
1173     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
1174     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
1175     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
1176     n = 4;
1177     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
1178     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1179     n = 4;
1180     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
1181     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1182     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
1183     if (!useLabels) numLabels = 0;
1184     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
1185     if (!useColors) {
1186       numColors = 3;
1187       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
1188     }
1189     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
1190     if (!useColors) {
1191       numLColors = 4;
1192       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
1193     }
1194     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
1195     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
1196     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
1197     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
1198     if (depth < dim) plotEdges = PETSC_FALSE;
1199     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
1200 
1201     /* filter points with labelvalue != labeldefaultvalue */
1202     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1203     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1204     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1205     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1206     PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
1207     if (lflg) {
1208       DMLabel lbl;
1209 
1210       PetscCall(DMGetLabel(dm, lname, &lbl));
1211       if (lbl) {
1212         PetscInt val, defval;
1213 
1214         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1215         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1216         for (c = pStart; c < pEnd; c++) {
1217           PetscInt *closure = NULL;
1218           PetscInt  closureSize;
1219 
1220           PetscCall(DMLabelGetValue(lbl, c, &val));
1221           if (val == defval) continue;
1222 
1223           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1224           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1225           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1226         }
1227       }
1228     }
1229 
1230     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1231     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1232     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1233     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1234 \\documentclass[tikz]{standalone}\n\n\
1235 \\usepackage{pgflibraryshapes}\n\
1236 \\usetikzlibrary{backgrounds}\n\
1237 \\usetikzlibrary{arrows}\n\
1238 \\begin{document}\n"));
1239     if (size > 1) {
1240       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1241       for (p = 0; p < size; ++p) {
1242         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1243         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1244       }
1245       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1246     }
1247     if (drawHasse) {
1248       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, PetscMax(fEnd - fStart, cEnd - cStart)));
1249 
1250       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1251       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1252       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1253       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1254       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1255       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1256       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1257       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1258       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fStart}{%" PetscInt_FMT "}\n", fStart));
1259       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fEnd}{%" PetscInt_FMT "}\n", fEnd - 1));
1260       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fShift}{%.2f}\n", 3 + (maxStratum - (fEnd - fStart)) / 2.));
1261       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numFaces}{%" PetscInt_FMT "}\n", fEnd - fStart));
1262       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1263       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1264       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1265       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1266     }
1267     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1268 
1269     /* Plot vertices */
1270     PetscCall(VecGetArray(coordinates, &coords));
1271     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1272     for (v = vStart; v < vEnd; ++v) {
1273       PetscInt  off, dof, d;
1274       PetscBool isLabeled = PETSC_FALSE;
1275 
1276       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1277       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1278       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1279       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1280       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1281       for (d = 0; d < dof; ++d) {
1282         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1283         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1284       }
1285       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1286       if (dim == 3) {
1287         PetscReal tmp = tcoords[1];
1288         tcoords[1]    = tcoords[2];
1289         tcoords[2]    = -tmp;
1290       }
1291       for (d = 0; d < dof; ++d) {
1292         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1293         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", tcoords[d]));
1294       }
1295       if (drawHasse) color = colors[0 % numColors];
1296       else color = colors[rank % numColors];
1297       for (l = 0; l < numLabels; ++l) {
1298         PetscInt val;
1299         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1300         if (val >= 0) {
1301           color     = lcolors[l % numLColors];
1302           isLabeled = PETSC_TRUE;
1303           break;
1304         }
1305       }
1306       if (drawNumbers[0]) {
1307         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1308       } else if (drawColors[0]) {
1309         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1310       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1311     }
1312     PetscCall(VecRestoreArray(coordinates, &coords));
1313     PetscCall(PetscViewerFlush(viewer));
1314     /* Plot edges */
1315     if (plotEdges) {
1316       PetscCall(VecGetArray(coordinates, &coords));
1317       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1318       for (e = eStart; e < eEnd; ++e) {
1319         const PetscInt *cone;
1320         PetscInt        coneSize, offA, offB, dof, d;
1321 
1322         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1323         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1324         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1325         PetscCall(DMPlexGetCone(dm, e, &cone));
1326         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1327         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1328         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1329         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1330         for (d = 0; d < dof; ++d) {
1331           tcoords[d] = (double)(scale * PetscRealPart(coords[offA + d] + coords[offB + d]) / 2);
1332           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1333         }
1334         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1335         if (dim == 3) {
1336           PetscReal tmp = tcoords[1];
1337           tcoords[1]    = tcoords[2];
1338           tcoords[2]    = -tmp;
1339         }
1340         for (d = 0; d < dof; ++d) {
1341           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1342           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", tcoords[d]));
1343         }
1344         if (drawHasse) color = colors[1 % numColors];
1345         else color = colors[rank % numColors];
1346         for (l = 0; l < numLabels; ++l) {
1347           PetscInt val;
1348           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1349           if (val >= 0) {
1350             color = lcolors[l % numLColors];
1351             break;
1352           }
1353         }
1354         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1355       }
1356       PetscCall(VecRestoreArray(coordinates, &coords));
1357       PetscCall(PetscViewerFlush(viewer));
1358       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1359     }
1360     /* Plot cells */
1361     if (dim == 3 || !drawNumbers[1]) {
1362       for (e = eStart; e < eEnd; ++e) {
1363         const PetscInt *cone;
1364 
1365         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1366         color = colors[rank % numColors];
1367         for (l = 0; l < numLabels; ++l) {
1368           PetscInt val;
1369           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1370           if (val >= 0) {
1371             color = lcolors[l % numLColors];
1372             break;
1373           }
1374         }
1375         PetscCall(DMPlexGetCone(dm, e, &cone));
1376         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1377       }
1378     } else {
1379       DMPolytopeType ct;
1380 
1381       /* Drawing a 2D polygon */
1382       for (c = cStart; c < cEnd; ++c) {
1383         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1384         PetscCall(DMPlexGetCellType(dm, c, &ct));
1385         if (DMPolytopeTypeIsHybrid(ct)) {
1386           const PetscInt *cone;
1387           PetscInt        coneSize, e;
1388 
1389           PetscCall(DMPlexGetCone(dm, c, &cone));
1390           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1391           for (e = 0; e < coneSize; ++e) {
1392             const PetscInt *econe;
1393 
1394             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1395             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", colors[rank % numColors], econe[0], rank, cone[e], rank, econe[1], rank));
1396           }
1397         } else {
1398           PetscInt *closure = NULL;
1399           PetscInt  closureSize, Nv = 0, v;
1400 
1401           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1402           for (p = 0; p < closureSize * 2; p += 2) {
1403             const PetscInt point = closure[p];
1404 
1405             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1406           }
1407           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1408           for (v = 0; v <= Nv; ++v) {
1409             const PetscInt vertex = closure[v % Nv];
1410 
1411             if (v > 0) {
1412               if (plotEdges) {
1413                 const PetscInt *edge;
1414                 PetscInt        endpoints[2], ne;
1415 
1416                 endpoints[0] = closure[v - 1];
1417                 endpoints[1] = vertex;
1418                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1419                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1420                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1421                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1422               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1423             }
1424             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1425           }
1426           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1427           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1428         }
1429       }
1430     }
1431     for (c = cStart; c < cEnd; ++c) {
1432       double             ccoords[3] = {0.0, 0.0, 0.0};
1433       PetscBool          isLabeled  = PETSC_FALSE;
1434       PetscScalar       *cellCoords = NULL;
1435       const PetscScalar *array;
1436       PetscInt           numCoords, cdim, d;
1437       PetscBool          isDG;
1438 
1439       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1440       PetscCall(DMGetCoordinateDim(dm, &cdim));
1441       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1442       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1443       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1444       for (p = 0; p < numCoords / cdim; ++p) {
1445         for (d = 0; d < cdim; ++d) {
1446           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1447           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1448         }
1449         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1450         if (cdim == 3) {
1451           PetscReal tmp = tcoords[1];
1452           tcoords[1]    = tcoords[2];
1453           tcoords[2]    = -tmp;
1454         }
1455         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1456       }
1457       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1458       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1459       for (d = 0; d < cdim; ++d) {
1460         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1461         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", ccoords[d]));
1462       }
1463       if (drawHasse) color = colors[depth % numColors];
1464       else color = colors[rank % numColors];
1465       for (l = 0; l < numLabels; ++l) {
1466         PetscInt val;
1467         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1468         if (val >= 0) {
1469           color     = lcolors[l % numLColors];
1470           isLabeled = PETSC_TRUE;
1471           break;
1472         }
1473       }
1474       if (drawNumbers[dim]) {
1475         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1476       } else if (drawColors[dim]) {
1477         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1478       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1479     }
1480     if (drawHasse) {
1481       int height = 0;
1482 
1483       color = colors[depth % numColors];
1484       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1485       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1486       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1487       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,%d) {\\c};\n", rank, color, height++));
1488       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1489 
1490       if (depth > 2) {
1491         color = colors[1 % numColors];
1492         PetscCall(PetscViewerASCIIPrintf(viewer, "%% Faces\n"));
1493         PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\f in {\\fStart,...,\\fEnd}\n"));
1494         PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1495         PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\f_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\fShift+\\f-\\fStart,%d) {\\f};\n", rank, color, height++));
1496         PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1497       }
1498 
1499       color = colors[1 % numColors];
1500       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1501       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1502       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1503       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,%d) {\\e};\n", rank, color, height++));
1504       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1505 
1506       color = colors[0 % numColors];
1507       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1508       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1509       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1510       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,%d) {\\v};\n", rank, color, height++));
1511       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1512 
1513       for (p = pStart; p < pEnd; ++p) {
1514         const PetscInt *cone;
1515         PetscInt        coneSize, cp;
1516 
1517         PetscCall(DMPlexGetCone(dm, p, &cone));
1518         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1519         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1520       }
1521     }
1522     PetscCall(PetscViewerFlush(viewer));
1523     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1524     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1525     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1526     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1527     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1528     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1529     PetscCall(PetscFree3(names, colors, lcolors));
1530     PetscCall(PetscBTDestroy(&wp));
1531   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1532     Vec                    cown, acown;
1533     VecScatter             sct;
1534     ISLocalToGlobalMapping g2l;
1535     IS                     gid, acis;
1536     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1537     MPI_Group              ggroup, ngroup;
1538     PetscScalar           *array, nid;
1539     const PetscInt        *idxs;
1540     PetscInt              *idxs2, *start, *adjacency, *work;
1541     PetscInt64             lm[3], gm[3];
1542     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1543     PetscMPIInt            d1, d2, rank;
1544 
1545     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1546     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1547 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1548     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1549 #endif
1550     if (ncomm != MPI_COMM_NULL) {
1551       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1552       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1553       d1 = 0;
1554       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1555       nid = d2;
1556       PetscCallMPI(MPI_Group_free(&ggroup));
1557       PetscCallMPI(MPI_Group_free(&ngroup));
1558       PetscCallMPI(MPI_Comm_free(&ncomm));
1559     } else nid = 0.0;
1560 
1561     /* Get connectivity */
1562     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1563     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1564 
1565     /* filter overlapped local cells */
1566     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1567     PetscCall(ISGetIndices(gid, &idxs));
1568     PetscCall(ISGetLocalSize(gid, &cum));
1569     PetscCall(PetscMalloc1(cum, &idxs2));
1570     for (c = cStart, cum = 0; c < cEnd; c++) {
1571       if (idxs[c - cStart] < 0) continue;
1572       idxs2[cum++] = idxs[c - cStart];
1573     }
1574     PetscCall(ISRestoreIndices(gid, &idxs));
1575     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1576     PetscCall(ISDestroy(&gid));
1577     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1578 
1579     /* support for node-aware cell locality */
1580     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1581     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1582     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1583     PetscCall(VecGetArray(cown, &array));
1584     for (c = 0; c < numVertices; c++) array[c] = nid;
1585     PetscCall(VecRestoreArray(cown, &array));
1586     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1587     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1588     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1589     PetscCall(ISDestroy(&acis));
1590     PetscCall(VecScatterDestroy(&sct));
1591     PetscCall(VecDestroy(&cown));
1592 
1593     /* compute edgeCut */
1594     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1595     PetscCall(PetscMalloc1(cum, &work));
1596     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1597     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1598     PetscCall(ISDestroy(&gid));
1599     PetscCall(VecGetArray(acown, &array));
1600     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1601       PetscInt totl;
1602 
1603       totl = start[c + 1] - start[c];
1604       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1605       for (i = 0; i < totl; i++) {
1606         if (work[i] < 0) {
1607           ect += 1;
1608           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1609         }
1610       }
1611     }
1612     PetscCall(PetscFree(work));
1613     PetscCall(VecRestoreArray(acown, &array));
1614     lm[0] = numVertices > 0 ? numVertices : PETSC_INT_MAX;
1615     lm[1] = -numVertices;
1616     PetscCallMPI(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1617     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt64_FMT ", min %" PetscInt64_FMT, -((double)gm[1]) / ((double)gm[0]), -gm[1], gm[0]));
1618     lm[0] = ect;                     /* edgeCut */
1619     lm[1] = ectn;                    /* node-aware edgeCut */
1620     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1621     PetscCallMPI(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1622     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt64_FMT ")\n", gm[2]));
1623 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1624     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt64_FMT " (on node %.3f)\n", gm[0] / 2, gm[0] ? ((double)gm[1]) / ((double)gm[0]) : 1.));
1625 #else
1626     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt64_FMT " (on node %.3f)\n", gm[0] / 2, 0.0));
1627 #endif
1628     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1629     PetscCall(PetscFree(start));
1630     PetscCall(PetscFree(adjacency));
1631     PetscCall(VecDestroy(&acown));
1632   } else {
1633     const char    *name;
1634     PetscInt      *sizes, *hybsizes, *ghostsizes;
1635     PetscInt       locDepth, depth, cellHeight, dim, d;
1636     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1637     PetscInt       numLabels, l, maxSize = 17;
1638     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1639     MPI_Comm       comm;
1640     PetscMPIInt    size, rank;
1641 
1642     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1643     PetscCallMPI(MPI_Comm_size(comm, &size));
1644     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1645     PetscCall(DMGetDimension(dm, &dim));
1646     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1647     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1648     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1649     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1650     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1651     PetscCall(DMPlexGetDepth(dm, &locDepth));
1652     PetscCallMPI(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1653     PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &gcStart, &gcEnd));
1654     gcNum = gcEnd - gcStart;
1655     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1656     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1657     for (d = 0; d <= depth; d++) {
1658       PetscInt Nc[2] = {0, 0}, ict;
1659 
1660       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1661       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1662       ict = ct0;
1663       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1664       ct0 = (DMPolytopeType)ict;
1665       for (p = pStart; p < pEnd; ++p) {
1666         DMPolytopeType ct;
1667 
1668         PetscCall(DMPlexGetCellType(dm, p, &ct));
1669         if (ct == ct0) ++Nc[0];
1670         else ++Nc[1];
1671       }
1672       if (size < maxSize) {
1673         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1674         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1675         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1676         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1677         for (p = 0; p < size; ++p) {
1678           if (rank == 0) {
1679             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1680             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1681             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1682           }
1683         }
1684       } else {
1685         PetscInt locMinMax[2];
1686 
1687         locMinMax[0] = Nc[0] + Nc[1];
1688         locMinMax[1] = Nc[0] + Nc[1];
1689         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1690         locMinMax[0] = Nc[1];
1691         locMinMax[1] = Nc[1];
1692         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1693         if (d == depth) {
1694           locMinMax[0] = gcNum;
1695           locMinMax[1] = gcNum;
1696           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1697         }
1698         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1699         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1700         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1701         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1702       }
1703       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1704     }
1705     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1706     {
1707       const PetscReal *maxCell;
1708       const PetscReal *L;
1709       PetscBool        localized;
1710 
1711       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1712       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1713       if (L || localized) {
1714         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1715         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1716         if (L) {
1717           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1718           for (d = 0; d < dim; ++d) {
1719             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1720             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1721           }
1722           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1723         }
1724         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1725         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1726       }
1727     }
1728     PetscCall(DMGetNumLabels(dm, &numLabels));
1729     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1730     for (l = 0; l < numLabels; ++l) {
1731       DMLabel     label;
1732       const char *name;
1733       PetscInt   *values;
1734       PetscInt    numValues, v;
1735 
1736       PetscCall(DMGetLabelName(dm, l, &name));
1737       PetscCall(DMGetLabel(dm, name, &label));
1738       PetscCall(DMLabelGetNumValues(label, &numValues));
1739       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1740 
1741       { // Extract array of DMLabel values so it can be sorted
1742         IS              is_values;
1743         const PetscInt *is_values_local = NULL;
1744 
1745         PetscCall(DMLabelGetValueIS(label, &is_values));
1746         PetscCall(ISGetIndices(is_values, &is_values_local));
1747         PetscCall(PetscMalloc1(numValues, &values));
1748         PetscCall(PetscArraycpy(values, is_values_local, numValues));
1749         PetscCall(PetscSortInt(numValues, values));
1750         PetscCall(ISRestoreIndices(is_values, &is_values_local));
1751         PetscCall(ISDestroy(&is_values));
1752       }
1753       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1754       for (v = 0; v < numValues; ++v) {
1755         PetscInt size;
1756 
1757         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1758         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1759         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1760       }
1761       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1762       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1763       PetscCall(PetscFree(values));
1764     }
1765     {
1766       char    **labelNames;
1767       PetscInt  Nl = numLabels;
1768       PetscBool flg;
1769 
1770       PetscCall(PetscMalloc1(Nl, &labelNames));
1771       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1772       for (l = 0; l < Nl; ++l) {
1773         DMLabel label;
1774 
1775         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1776         if (flg) {
1777           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1778           PetscCall(DMLabelView(label, viewer));
1779         }
1780         PetscCall(PetscFree(labelNames[l]));
1781       }
1782       PetscCall(PetscFree(labelNames));
1783     }
1784     /* If no fields are specified, people do not want to see adjacency */
1785     if (dm->Nf) {
1786       PetscInt f;
1787 
1788       for (f = 0; f < dm->Nf; ++f) {
1789         const char *name;
1790 
1791         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1792         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1793         PetscCall(PetscViewerASCIIPushTab(viewer));
1794         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1795         if (dm->fields[f].adjacency[0]) {
1796           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1797           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1798         } else {
1799           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1800           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1801         }
1802         PetscCall(PetscViewerASCIIPopTab(viewer));
1803       }
1804     }
1805     DMPlexTransform tr;
1806 
1807     PetscCall(DMPlexGetTransform(dm, &tr));
1808     if (tr) {
1809       PetscCall(PetscViewerASCIIPushTab(viewer));
1810       PetscCall(PetscViewerASCIIPrintf(viewer, "Created using transform:\n"));
1811       PetscCall(DMPlexTransformView(tr, viewer));
1812       PetscCall(PetscViewerASCIIPopTab(viewer));
1813     }
1814     PetscCall(DMGetCoarseDM(dm, &cdm));
1815     if (cdm) {
1816       PetscCall(PetscViewerASCIIPushTab(viewer));
1817       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1818       PetscCall(DMPlexView_Ascii(cdm, viewer));
1819       PetscCall(PetscViewerASCIIPopTab(viewer));
1820     }
1821   }
1822   PetscFunctionReturn(PETSC_SUCCESS);
1823 }
1824 
1825 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt lC, PetscInt cC, PetscInt cell, const PetscScalar coords[])
1826 {
1827   DMPolytopeType ct;
1828   PetscMPIInt    rank;
1829   PetscInt       cdim;
1830   int            lineColor, cellColor;
1831 
1832   PetscFunctionBegin;
1833   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1834   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1835   PetscCall(DMGetCoordinateDim(dm, &cdim));
1836   lineColor = (int)(lC < 0 ? PETSC_DRAW_BLACK : lC);
1837   cellColor = (int)(cC < 0 ? PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2 : cC);
1838   switch (ct) {
1839   case DM_POLYTOPE_SEGMENT:
1840   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1841     switch (cdim) {
1842     case 1: {
1843       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1844       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1845 
1846       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, lineColor));
1847       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, lineColor));
1848       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, lineColor));
1849     } break;
1850     case 2: {
1851       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1852       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1853       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1854 
1855       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), lineColor));
1856       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]) + l * dx, PetscRealPart(coords[1]) + l * dy, PetscRealPart(coords[0]) - l * dx, PetscRealPart(coords[1]) - l * dy, lineColor));
1857       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]) + l * dx, PetscRealPart(coords[3]) + l * dy, PetscRealPart(coords[2]) - l * dx, PetscRealPart(coords[3]) - l * dy, lineColor));
1858     } break;
1859     default:
1860       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1861     }
1862     break;
1863   case DM_POLYTOPE_TRIANGLE:
1864     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), cellColor, cellColor, cellColor));
1865     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), lineColor));
1866     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), lineColor));
1867     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), lineColor));
1868     break;
1869   case DM_POLYTOPE_QUADRILATERAL:
1870     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), cellColor, cellColor, cellColor));
1871     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), cellColor, cellColor, cellColor));
1872     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), lineColor));
1873     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), lineColor));
1874     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), lineColor));
1875     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), lineColor));
1876     break;
1877   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1878     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), cellColor, cellColor, cellColor));
1879     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), cellColor, cellColor, cellColor));
1880     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), lineColor));
1881     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), lineColor));
1882     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), lineColor));
1883     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), lineColor));
1884     break;
1885   case DM_POLYTOPE_FV_GHOST:
1886     break;
1887   default:
1888     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1889   }
1890   PetscFunctionReturn(PETSC_SUCCESS);
1891 }
1892 
1893 static PetscErrorCode DrawPolygon_Private(DM dm, PetscDraw draw, PetscInt cell, PetscInt Nv, const PetscReal refVertices[], const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1894 {
1895   PetscReal   centroid[2] = {0., 0.};
1896   PetscMPIInt rank;
1897   PetscMPIInt fillColor;
1898 
1899   PetscFunctionBegin;
1900   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1901   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1902   for (PetscInt v = 0; v < Nv; ++v) {
1903     centroid[0] += PetscRealPart(coords[v * 2 + 0]) / Nv;
1904     centroid[1] += PetscRealPart(coords[v * 2 + 1]) / Nv;
1905   }
1906   for (PetscInt e = 0; e < Nv; ++e) {
1907     refCoords[0] = refVertices[e * 2 + 0];
1908     refCoords[1] = refVertices[e * 2 + 1];
1909     for (PetscInt d = 1; d <= edgeDiv; ++d) {
1910       refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % Nv * 2 + 0] - refCoords[0]) * d / edgeDiv;
1911       refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % Nv * 2 + 1] - refCoords[1]) * d / edgeDiv;
1912     }
1913     PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1914     for (PetscInt d = 0; d < edgeDiv; ++d) {
1915       PetscCall(PetscDrawTriangle(draw, centroid[0], centroid[1], edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], fillColor, fillColor, fillColor));
1916       PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1917     }
1918   }
1919   PetscFunctionReturn(PETSC_SUCCESS);
1920 }
1921 
1922 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1923 {
1924   DMPolytopeType ct;
1925 
1926   PetscFunctionBegin;
1927   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1928   switch (ct) {
1929   case DM_POLYTOPE_TRIANGLE: {
1930     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1931 
1932     PetscCall(DrawPolygon_Private(dm, draw, cell, 3, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1933   } break;
1934   case DM_POLYTOPE_QUADRILATERAL: {
1935     PetscReal refVertices[8] = {-1., -1., 1., -1., 1., 1., -1., 1.};
1936 
1937     PetscCall(DrawPolygon_Private(dm, draw, cell, 4, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1938   } break;
1939   default:
1940     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1941   }
1942   PetscFunctionReturn(PETSC_SUCCESS);
1943 }
1944 
1945 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1946 {
1947   PetscDraw    draw;
1948   DM           cdm;
1949   PetscSection coordSection;
1950   Vec          coordinates;
1951   PetscReal    xyl[3], xyr[3];
1952   PetscReal   *refCoords, *edgeCoords;
1953   PetscBool    isnull, drawAffine;
1954   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, cDegree, edgeDiv, lineColor = PETSC_DETERMINE, cellColor = PETSC_DETERMINE;
1955 
1956   PetscFunctionBegin;
1957   PetscCall(DMGetCoordinateDim(dm, &dim));
1958   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1959   PetscCall(DMGetCoordinateDegree_Internal(dm, &cDegree));
1960   drawAffine = cDegree > 1 ? PETSC_FALSE : PETSC_TRUE;
1961   edgeDiv    = cDegree + 1;
1962   PetscCall(PetscOptionsGetInt(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_line_color", &lineColor, NULL));
1963   PetscCall(PetscOptionsGetInt(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_cell_color", &cellColor, NULL));
1964   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1965   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1966   PetscCall(DMGetCoordinateDM(dm, &cdm));
1967   PetscCall(DMGetLocalSection(cdm, &coordSection));
1968   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1969   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1970   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1971 
1972   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1973   PetscCall(PetscDrawIsNull(draw, &isnull));
1974   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1975   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1976 
1977   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1978   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1979   PetscCall(PetscDrawClear(draw));
1980 
1981   for (c = cStart; c < cEnd; ++c) {
1982     PetscScalar       *coords = NULL;
1983     const PetscScalar *coords_arr;
1984     PetscInt           numCoords;
1985     PetscBool          isDG;
1986 
1987     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1988     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, lineColor, cellColor, c, coords));
1989     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1990     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1991   }
1992   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1993   PetscCall(PetscDrawFlush(draw));
1994   PetscCall(PetscDrawPause(draw));
1995   PetscCall(PetscDrawSave(draw));
1996   PetscFunctionReturn(PETSC_SUCCESS);
1997 }
1998 
1999 static PetscErrorCode DMPlexCreateHighOrderSurrogate_Internal(DM dm, DM *hdm)
2000 {
2001   DM           odm = dm, rdm = dm, cdm;
2002   PetscFE      fe;
2003   PetscSpace   sp;
2004   PetscClassId id;
2005   PetscInt     degree;
2006   PetscBool    hoView = PETSC_TRUE;
2007 
2008   PetscFunctionBegin;
2009   PetscObjectOptionsBegin((PetscObject)dm);
2010   PetscCall(PetscOptionsBool("-dm_plex_high_order_view", "Subsample to view meshes with high order coordinates", "DMPlexCreateHighOrderSurrogate_Internal", hoView, &hoView, NULL));
2011   PetscOptionsEnd();
2012   PetscCall(PetscObjectReference((PetscObject)dm));
2013   *hdm = dm;
2014   if (!hoView) PetscFunctionReturn(PETSC_SUCCESS);
2015   PetscCall(DMGetCoordinateDM(dm, &cdm));
2016   PetscCall(DMGetField(cdm, 0, NULL, (PetscObject *)&fe));
2017   PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
2018   if (id != PETSCFE_CLASSID) PetscFunctionReturn(PETSC_SUCCESS);
2019   PetscCall(PetscFEGetBasisSpace(fe, &sp));
2020   PetscCall(PetscSpaceGetDegree(sp, &degree, NULL));
2021   for (PetscInt r = 0, rd = PetscCeilReal(((PetscReal)degree) / 2.); r < (PetscInt)PetscCeilReal(PetscLog2Real(degree)); ++r, rd = PetscCeilReal(((PetscReal)rd) / 2.)) {
2022     DM  cdm, rcdm;
2023     Mat In;
2024     Vec cl, rcl;
2025 
2026     PetscCall(DMRefine(odm, PetscObjectComm((PetscObject)odm), &rdm));
2027     PetscCall(DMPlexCreateCoordinateSpace(rdm, rd, PETSC_FALSE, NULL));
2028     PetscCall(PetscObjectSetName((PetscObject)rdm, "Refined Mesh with Linear Coordinates"));
2029     PetscCall(DMGetCoordinateDM(odm, &cdm));
2030     PetscCall(DMGetCoordinateDM(rdm, &rcdm));
2031     PetscCall(DMGetCoordinatesLocal(odm, &cl));
2032     PetscCall(DMGetCoordinatesLocal(rdm, &rcl));
2033     PetscCall(DMSetCoarseDM(rcdm, cdm));
2034     PetscCall(DMCreateInterpolation(cdm, rcdm, &In, NULL));
2035     PetscCall(MatMult(In, cl, rcl));
2036     PetscCall(MatDestroy(&In));
2037     PetscCall(DMSetCoordinatesLocal(rdm, rcl));
2038     PetscCall(DMDestroy(&odm));
2039     odm = rdm;
2040   }
2041   *hdm = rdm;
2042   PetscFunctionReturn(PETSC_SUCCESS);
2043 }
2044 
2045 #if defined(PETSC_HAVE_EXODUSII)
2046   #include <exodusII.h>
2047   #include <petscviewerexodusii.h>
2048 #endif
2049 
2050 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
2051 {
2052   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns, ispython;
2053   char      name[PETSC_MAX_PATH_LEN];
2054 
2055   PetscFunctionBegin;
2056   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2057   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2058   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
2059   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
2060   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2061   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
2062   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
2063   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
2064   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
2065   PetscCall(PetscObjectHasFunction((PetscObject)viewer, "PetscViewerPythonViewObject_C", &ispython));
2066   if (iascii) {
2067     PetscViewerFormat format;
2068     PetscCall(PetscViewerGetFormat(viewer, &format));
2069     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
2070     else PetscCall(DMPlexView_Ascii(dm, viewer));
2071   } else if (ishdf5) {
2072 #if defined(PETSC_HAVE_HDF5)
2073     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
2074 #else
2075     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2076 #endif
2077   } else if (isvtk) {
2078     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
2079   } else if (isdraw) {
2080     DM hdm;
2081 
2082     PetscCall(DMPlexCreateHighOrderSurrogate_Internal(dm, &hdm));
2083     PetscCall(DMPlexView_Draw(hdm, viewer));
2084     PetscCall(DMDestroy(&hdm));
2085   } else if (isglvis) {
2086     PetscCall(DMPlexView_GLVis(dm, viewer));
2087 #if defined(PETSC_HAVE_EXODUSII)
2088   } else if (isexodus) {
2089     /*
2090       exodusII requires that all sets be part of exactly one cell set.
2091       If the dm does not have a "Cell Sets" label defined, we create one
2092       with ID 1, containing all cells.
2093       Note that if the Cell Sets label is defined but does not cover all cells,
2094       we may still have a problem. This should probably be checked here or in the viewer;
2095     */
2096     PetscInt numCS;
2097     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
2098     if (!numCS) {
2099       PetscInt cStart, cEnd, c;
2100       PetscCall(DMCreateLabel(dm, "Cell Sets"));
2101       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
2102       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
2103     }
2104     PetscCall(DMView_PlexExodusII(dm, viewer));
2105 #endif
2106 #if defined(PETSC_HAVE_CGNS)
2107   } else if (iscgns) {
2108     PetscCall(DMView_PlexCGNS(dm, viewer));
2109 #endif
2110   } else if (ispython) {
2111     PetscCall(PetscViewerPythonViewObject(viewer, (PetscObject)dm));
2112   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
2113   /* Optionally view the partition */
2114   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
2115   if (flg) {
2116     Vec ranks;
2117     PetscCall(DMPlexCreateRankField(dm, &ranks));
2118     PetscCall(VecView(ranks, viewer));
2119     PetscCall(VecDestroy(&ranks));
2120   }
2121   /* Optionally view a label */
2122   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
2123   if (flg) {
2124     DMLabel label;
2125     Vec     val;
2126 
2127     PetscCall(DMGetLabel(dm, name, &label));
2128     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
2129     PetscCall(DMPlexCreateLabelField(dm, label, &val));
2130     PetscCall(VecView(val, viewer));
2131     PetscCall(VecDestroy(&val));
2132   }
2133   PetscFunctionReturn(PETSC_SUCCESS);
2134 }
2135 
2136 /*@
2137   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
2138 
2139   Collective
2140 
2141   Input Parameters:
2142 + dm     - The `DM` whose topology is to be saved
2143 - viewer - The `PetscViewer` to save it in
2144 
2145   Level: advanced
2146 
2147 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
2148 @*/
2149 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
2150 {
2151   PetscBool ishdf5;
2152 
2153   PetscFunctionBegin;
2154   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2155   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2156   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2157   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
2158   if (ishdf5) {
2159 #if defined(PETSC_HAVE_HDF5)
2160     PetscViewerFormat format;
2161     PetscCall(PetscViewerGetFormat(viewer, &format));
2162     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2163       IS globalPointNumbering;
2164 
2165       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2166       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
2167       PetscCall(ISDestroy(&globalPointNumbering));
2168     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2169 #else
2170     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2171 #endif
2172   }
2173   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
2174   PetscFunctionReturn(PETSC_SUCCESS);
2175 }
2176 
2177 /*@
2178   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
2179 
2180   Collective
2181 
2182   Input Parameters:
2183 + dm     - The `DM` whose coordinates are to be saved
2184 - viewer - The `PetscViewer` for saving
2185 
2186   Level: advanced
2187 
2188 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
2189 @*/
2190 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
2191 {
2192   PetscBool ishdf5;
2193 
2194   PetscFunctionBegin;
2195   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2196   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2197   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2198   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2199   if (ishdf5) {
2200 #if defined(PETSC_HAVE_HDF5)
2201     PetscViewerFormat format;
2202     PetscCall(PetscViewerGetFormat(viewer, &format));
2203     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2204       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
2205     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2206 #else
2207     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2208 #endif
2209   }
2210   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2211   PetscFunctionReturn(PETSC_SUCCESS);
2212 }
2213 
2214 /*@
2215   DMPlexLabelsView - Saves `DMPLEX` labels into a file
2216 
2217   Collective
2218 
2219   Input Parameters:
2220 + dm     - The `DM` whose labels are to be saved
2221 - viewer - The `PetscViewer` for saving
2222 
2223   Level: advanced
2224 
2225 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
2226 @*/
2227 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
2228 {
2229   PetscBool ishdf5;
2230 
2231   PetscFunctionBegin;
2232   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2233   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2234   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2235   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
2236   if (ishdf5) {
2237 #if defined(PETSC_HAVE_HDF5)
2238     IS                globalPointNumbering;
2239     PetscViewerFormat format;
2240 
2241     PetscCall(PetscViewerGetFormat(viewer, &format));
2242     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2243       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2244       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
2245       PetscCall(ISDestroy(&globalPointNumbering));
2246     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2247 #else
2248     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2249 #endif
2250   }
2251   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
2252   PetscFunctionReturn(PETSC_SUCCESS);
2253 }
2254 
2255 /*@
2256   DMPlexSectionView - Saves a section associated with a `DMPLEX`
2257 
2258   Collective
2259 
2260   Input Parameters:
2261 + dm        - The `DM` that contains the topology on which the section to be saved is defined
2262 . viewer    - The `PetscViewer` for saving
2263 - sectiondm - The `DM` that contains the section to be saved, can be `NULL`
2264 
2265   Level: advanced
2266 
2267   Notes:
2268   This function is a wrapper around `PetscSectionView()`; in addition to the raw section, it saves information that associates the section points to the topology (`dm`) points. When the topology (`dm`) and the section are later loaded with `DMPlexTopologyLoad()` and `DMPlexSectionLoad()`, respectively, this information is used to match section points with topology points.
2269 
2270   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2271 
2272 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
2273 @*/
2274 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
2275 {
2276   PetscBool ishdf5;
2277 
2278   PetscFunctionBegin;
2279   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2280   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2281   if (!sectiondm) sectiondm = dm;
2282   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2283   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2284   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
2285   if (ishdf5) {
2286 #if defined(PETSC_HAVE_HDF5)
2287     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
2288 #else
2289     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2290 #endif
2291   }
2292   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
2293   PetscFunctionReturn(PETSC_SUCCESS);
2294 }
2295 
2296 /*@
2297   DMPlexGlobalVectorView - Saves a global vector
2298 
2299   Collective
2300 
2301   Input Parameters:
2302 + dm        - The `DM` that represents the topology
2303 . viewer    - The `PetscViewer` to save data with
2304 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2305 - vec       - The global vector to be saved
2306 
2307   Level: advanced
2308 
2309   Notes:
2310   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2311 
2312   Calling sequence:
2313 .vb
2314        DMCreate(PETSC_COMM_WORLD, &dm);
2315        DMSetType(dm, DMPLEX);
2316        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2317        DMClone(dm, &sectiondm);
2318        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2319        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2320        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2321        PetscSectionSetChart(section, pStart, pEnd);
2322        PetscSectionSetUp(section);
2323        DMSetLocalSection(sectiondm, section);
2324        PetscSectionDestroy(&section);
2325        DMGetGlobalVector(sectiondm, &vec);
2326        PetscObjectSetName((PetscObject)vec, "vec_name");
2327        DMPlexTopologyView(dm, viewer);
2328        DMPlexSectionView(dm, viewer, sectiondm);
2329        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2330        DMRestoreGlobalVector(sectiondm, &vec);
2331        DMDestroy(&sectiondm);
2332        DMDestroy(&dm);
2333 .ve
2334 
2335 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2336 @*/
2337 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2338 {
2339   PetscBool ishdf5;
2340 
2341   PetscFunctionBegin;
2342   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2343   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2344   if (!sectiondm) sectiondm = dm;
2345   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2346   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2347   /* Check consistency */
2348   {
2349     PetscSection section;
2350     PetscBool    includesConstraints;
2351     PetscInt     m, m1;
2352 
2353     PetscCall(VecGetLocalSize(vec, &m1));
2354     PetscCall(DMGetGlobalSection(sectiondm, &section));
2355     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2356     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2357     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2358     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2359   }
2360   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2361   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2362   if (ishdf5) {
2363 #if defined(PETSC_HAVE_HDF5)
2364     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2365 #else
2366     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2367 #endif
2368   }
2369   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2370   PetscFunctionReturn(PETSC_SUCCESS);
2371 }
2372 
2373 /*@
2374   DMPlexLocalVectorView - Saves a local vector
2375 
2376   Collective
2377 
2378   Input Parameters:
2379 + dm        - The `DM` that represents the topology
2380 . viewer    - The `PetscViewer` to save data with
2381 . sectiondm - The `DM` that contains the local section on which `vec` is defined, can be `NULL`
2382 - vec       - The local vector to be saved
2383 
2384   Level: advanced
2385 
2386   Note:
2387   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2388 
2389   Calling sequence:
2390 .vb
2391        DMCreate(PETSC_COMM_WORLD, &dm);
2392        DMSetType(dm, DMPLEX);
2393        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2394        DMClone(dm, &sectiondm);
2395        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2396        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2397        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2398        PetscSectionSetChart(section, pStart, pEnd);
2399        PetscSectionSetUp(section);
2400        DMSetLocalSection(sectiondm, section);
2401        DMGetLocalVector(sectiondm, &vec);
2402        PetscObjectSetName((PetscObject)vec, "vec_name");
2403        DMPlexTopologyView(dm, viewer);
2404        DMPlexSectionView(dm, viewer, sectiondm);
2405        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2406        DMRestoreLocalVector(sectiondm, &vec);
2407        DMDestroy(&sectiondm);
2408        DMDestroy(&dm);
2409 .ve
2410 
2411 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2412 @*/
2413 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2414 {
2415   PetscBool ishdf5;
2416 
2417   PetscFunctionBegin;
2418   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2419   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2420   if (!sectiondm) sectiondm = dm;
2421   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2422   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2423   /* Check consistency */
2424   {
2425     PetscSection section;
2426     PetscBool    includesConstraints;
2427     PetscInt     m, m1;
2428 
2429     PetscCall(VecGetLocalSize(vec, &m1));
2430     PetscCall(DMGetLocalSection(sectiondm, &section));
2431     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2432     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2433     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2434     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2435   }
2436   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2437   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2438   if (ishdf5) {
2439 #if defined(PETSC_HAVE_HDF5)
2440     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2441 #else
2442     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2443 #endif
2444   }
2445   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2446   PetscFunctionReturn(PETSC_SUCCESS);
2447 }
2448 
2449 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2450 {
2451   PetscBool ishdf5;
2452 
2453   PetscFunctionBegin;
2454   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2455   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2456   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2457   if (ishdf5) {
2458 #if defined(PETSC_HAVE_HDF5)
2459     PetscViewerFormat format;
2460     PetscCall(PetscViewerGetFormat(viewer, &format));
2461     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2462       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2463     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2464       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2465     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2466     PetscFunctionReturn(PETSC_SUCCESS);
2467 #else
2468     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2469 #endif
2470   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2471 }
2472 
2473 /*@
2474   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2475 
2476   Collective
2477 
2478   Input Parameters:
2479 + dm     - The `DM` into which the topology is loaded
2480 - viewer - The `PetscViewer` for the saved topology
2481 
2482   Output Parameter:
2483 . globalToLocalPointSF - The `PetscSF` that pushes points in [0, N) to the associated points in the loaded `DMPLEX`, where N is the global number of points;
2484   `NULL` if unneeded
2485 
2486   Level: advanced
2487 
2488 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2489           `PetscViewer`, `PetscSF`
2490 @*/
2491 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2492 {
2493   PetscBool ishdf5;
2494 
2495   PetscFunctionBegin;
2496   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2497   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2498   if (globalToLocalPointSF) PetscAssertPointer(globalToLocalPointSF, 3);
2499   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2500   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2501   if (ishdf5) {
2502 #if defined(PETSC_HAVE_HDF5)
2503     PetscViewerFormat format;
2504     PetscCall(PetscViewerGetFormat(viewer, &format));
2505     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2506       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2507     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2508 #else
2509     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2510 #endif
2511   }
2512   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2513   PetscFunctionReturn(PETSC_SUCCESS);
2514 }
2515 
2516 /*@
2517   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2518 
2519   Collective
2520 
2521   Input Parameters:
2522 + dm                   - The `DM` into which the coordinates are loaded
2523 . viewer               - The `PetscViewer` for the saved coordinates
2524 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2525 
2526   Level: advanced
2527 
2528 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2529           `PetscSF`, `PetscViewer`
2530 @*/
2531 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2532 {
2533   PetscBool ishdf5;
2534 
2535   PetscFunctionBegin;
2536   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2537   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2538   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2539   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2540   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2541   if (ishdf5) {
2542 #if defined(PETSC_HAVE_HDF5)
2543     PetscViewerFormat format;
2544     PetscCall(PetscViewerGetFormat(viewer, &format));
2545     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2546       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2547     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2548 #else
2549     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2550 #endif
2551   }
2552   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2553   PetscFunctionReturn(PETSC_SUCCESS);
2554 }
2555 
2556 /*@
2557   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2558 
2559   Collective
2560 
2561   Input Parameters:
2562 + dm                   - The `DM` into which the labels are loaded
2563 . viewer               - The `PetscViewer` for the saved labels
2564 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2565 
2566   Level: advanced
2567 
2568   Note:
2569   The `PetscSF` argument must not be `NULL` if the `DM` is distributed, otherwise an error occurs.
2570 
2571 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2572           `PetscSF`, `PetscViewer`
2573 @*/
2574 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2575 {
2576   PetscBool ishdf5;
2577 
2578   PetscFunctionBegin;
2579   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2580   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2581   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2582   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2583   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2584   if (ishdf5) {
2585 #if defined(PETSC_HAVE_HDF5)
2586     PetscViewerFormat format;
2587 
2588     PetscCall(PetscViewerGetFormat(viewer, &format));
2589     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2590       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2591     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2592 #else
2593     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2594 #endif
2595   }
2596   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2597   PetscFunctionReturn(PETSC_SUCCESS);
2598 }
2599 
2600 /*@
2601   DMPlexSectionLoad - Loads section into a `DMPLEX`
2602 
2603   Collective
2604 
2605   Input Parameters:
2606 + dm                   - The `DM` that represents the topology
2607 . viewer               - The `PetscViewer` that represents the on-disk section (sectionA)
2608 . sectiondm            - The `DM` into which the on-disk section (sectionA) is migrated, can be `NULL`
2609 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2610 
2611   Output Parameters:
2612 + globalDofSF - The `PetscSF` that migrates any on-disk `Vec` data associated with sectionA into a global `Vec` associated with the `sectiondm`'s global section (`NULL` if not needed)
2613 - localDofSF  - The `PetscSF` that migrates any on-disk `Vec` data associated with sectionA into a local `Vec` associated with the `sectiondm`'s local section (`NULL` if not needed)
2614 
2615   Level: advanced
2616 
2617   Notes:
2618   This function is a wrapper around `PetscSectionLoad()`; it loads, in addition to the raw section, a list of global point numbers that associates each on-disk section point with a global point number in [0, NX), where NX is the number of topology points in `dm`. Noting that globalToLocalPointSF associates each topology point in dm with a global number in [0, NX), one can readily establish an association of the on-disk section points with the topology points.
2619 
2620   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2621 
2622   The output parameter, `globalDofSF` (`localDofSF`), can later be used with `DMPlexGlobalVectorLoad()` (`DMPlexLocalVectorLoad()`) to load on-disk vectors into global (local) vectors associated with sectiondm's global (local) section.
2623 
2624   Example using 2 processes:
2625 .vb
2626   NX (number of points on dm): 4
2627   sectionA                   : the on-disk section
2628   vecA                       : a vector associated with sectionA
2629   sectionB                   : sectiondm's local section constructed in this function
2630   vecB (local)               : a vector associated with sectiondm's local section
2631   vecB (global)              : a vector associated with sectiondm's global section
2632 
2633                                      rank 0    rank 1
2634   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2635   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2636   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2637   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2638   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2639   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2640   sectionB->atlasDof             :     1 0 1 | 1 3
2641   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2642   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2643   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2644 .ve
2645   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2646 
2647 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2648 @*/
2649 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, PeOp DM sectiondm, PetscSF globalToLocalPointSF, PeOp PetscSF *globalDofSF, PeOp PetscSF *localDofSF)
2650 {
2651   PetscBool ishdf5;
2652 
2653   PetscFunctionBegin;
2654   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2655   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2656   if (!sectiondm) sectiondm = dm;
2657   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2658   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2659   if (globalDofSF) PetscAssertPointer(globalDofSF, 5);
2660   if (localDofSF) PetscAssertPointer(localDofSF, 6);
2661   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2662   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2663   if (ishdf5) {
2664 #if defined(PETSC_HAVE_HDF5)
2665     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2666 #else
2667     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2668 #endif
2669   }
2670   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2671   PetscFunctionReturn(PETSC_SUCCESS);
2672 }
2673 
2674 /*@
2675   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2676 
2677   Collective
2678 
2679   Input Parameters:
2680 + dm        - The `DM` that represents the topology
2681 . viewer    - The `PetscViewer` that represents the on-disk vector data
2682 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2683 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2684 - vec       - The global vector to set values of
2685 
2686   Level: advanced
2687 
2688   Notes:
2689   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2690 
2691   Calling sequence:
2692 .vb
2693        DMCreate(PETSC_COMM_WORLD, &dm);
2694        DMSetType(dm, DMPLEX);
2695        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2696        DMPlexTopologyLoad(dm, viewer, &sfX);
2697        DMClone(dm, &sectiondm);
2698        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2699        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2700        DMGetGlobalVector(sectiondm, &vec);
2701        PetscObjectSetName((PetscObject)vec, "vec_name");
2702        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2703        DMRestoreGlobalVector(sectiondm, &vec);
2704        PetscSFDestroy(&gsf);
2705        PetscSFDestroy(&sfX);
2706        DMDestroy(&sectiondm);
2707        DMDestroy(&dm);
2708 .ve
2709 
2710 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2711           `PetscSF`, `PetscViewer`
2712 @*/
2713 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2714 {
2715   PetscBool ishdf5;
2716 
2717   PetscFunctionBegin;
2718   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2719   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2720   if (!sectiondm) sectiondm = dm;
2721   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2722   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2723   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2724   /* Check consistency */
2725   {
2726     PetscSection section;
2727     PetscBool    includesConstraints;
2728     PetscInt     m, m1;
2729 
2730     PetscCall(VecGetLocalSize(vec, &m1));
2731     PetscCall(DMGetGlobalSection(sectiondm, &section));
2732     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2733     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2734     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2735     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2736   }
2737   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2738   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2739   if (ishdf5) {
2740 #if defined(PETSC_HAVE_HDF5)
2741     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2742 #else
2743     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2744 #endif
2745   }
2746   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2747   PetscFunctionReturn(PETSC_SUCCESS);
2748 }
2749 
2750 /*@
2751   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2752 
2753   Collective
2754 
2755   Input Parameters:
2756 + dm        - The `DM` that represents the topology
2757 . viewer    - The `PetscViewer` that represents the on-disk vector data
2758 . sectiondm - The `DM` that contains the local section on which vec is defined, can be `NULL`
2759 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2760 - vec       - The local vector to set values of
2761 
2762   Level: advanced
2763 
2764   Notes:
2765   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2766 
2767   Calling sequence:
2768 .vb
2769        DMCreate(PETSC_COMM_WORLD, &dm);
2770        DMSetType(dm, DMPLEX);
2771        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2772        DMPlexTopologyLoad(dm, viewer, &sfX);
2773        DMClone(dm, &sectiondm);
2774        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2775        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2776        DMGetLocalVector(sectiondm, &vec);
2777        PetscObjectSetName((PetscObject)vec, "vec_name");
2778        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2779        DMRestoreLocalVector(sectiondm, &vec);
2780        PetscSFDestroy(&lsf);
2781        PetscSFDestroy(&sfX);
2782        DMDestroy(&sectiondm);
2783        DMDestroy(&dm);
2784 .ve
2785 
2786 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2787           `PetscSF`, `PetscViewer`
2788 @*/
2789 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2790 {
2791   PetscBool ishdf5;
2792 
2793   PetscFunctionBegin;
2794   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2795   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2796   if (!sectiondm) sectiondm = dm;
2797   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2798   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2799   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2800   /* Check consistency */
2801   {
2802     PetscSection section;
2803     PetscBool    includesConstraints;
2804     PetscInt     m, m1;
2805 
2806     PetscCall(VecGetLocalSize(vec, &m1));
2807     PetscCall(DMGetLocalSection(sectiondm, &section));
2808     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2809     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2810     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2811     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2812   }
2813   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2814   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2815   if (ishdf5) {
2816 #if defined(PETSC_HAVE_HDF5)
2817     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2818 #else
2819     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2820 #endif
2821   }
2822   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2823   PetscFunctionReturn(PETSC_SUCCESS);
2824 }
2825 
2826 PetscErrorCode DMDestroy_Plex(DM dm)
2827 {
2828   DM_Plex *mesh = (DM_Plex *)dm->data;
2829 
2830   PetscFunctionBegin;
2831   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2832   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2833   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", NULL));
2834   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBounds_C", NULL));
2835   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2836   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2837   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2838   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2839   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2840   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2841   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2842   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2843   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetDefault_C", NULL));
2844   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetDefault_C", NULL));
2845   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetType_C", NULL));
2846   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetType_C", NULL));
2847   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2848   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2849   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetUseCeed_C", NULL));
2850   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetUseCeed_C", NULL));
2851   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2852   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2853   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2854   PetscCall(PetscFree(mesh->cones));
2855   PetscCall(PetscFree(mesh->coneOrientations));
2856   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2857   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2858   PetscCall(PetscFree(mesh->supports));
2859   PetscCall(PetscFree(mesh->cellTypes));
2860   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2861   PetscCall(PetscFree(mesh->tetgenOpts));
2862   PetscCall(PetscFree(mesh->triangleOpts));
2863   PetscCall(PetscFree(mesh->transformType));
2864   PetscCall(PetscFree(mesh->distributionName));
2865   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2866   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2867   PetscCall(ISDestroy(&mesh->subpointIS));
2868   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2869   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2870   if (mesh->periodic.face_sfs) {
2871     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFDestroy(&mesh->periodic.face_sfs[i]));
2872     PetscCall(PetscFree(mesh->periodic.face_sfs));
2873   }
2874   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2875   if (mesh->periodic.periodic_points) {
2876     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(ISDestroy(&mesh->periodic.periodic_points[i]));
2877     PetscCall(PetscFree(mesh->periodic.periodic_points));
2878   }
2879   if (mesh->periodic.transform) PetscCall(PetscFree(mesh->periodic.transform));
2880   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2881   PetscCall(ISDestroy(&mesh->anchorIS));
2882   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2883   PetscCall(PetscFree(mesh->parents));
2884   PetscCall(PetscFree(mesh->childIDs));
2885   PetscCall(PetscSectionDestroy(&mesh->childSection));
2886   PetscCall(PetscFree(mesh->children));
2887   PetscCall(DMDestroy(&mesh->referenceTree));
2888   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2889   PetscCall(PetscFree(mesh->neighbors));
2890   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2891   if (mesh->nonempty_comm != MPI_COMM_NULL && mesh->nonempty_comm != MPI_COMM_SELF) PetscCallMPI(MPI_Comm_free(&mesh->nonempty_comm));
2892   PetscCall(DMPlexTransformDestroy(&mesh->transform));
2893   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2894   PetscCall(PetscFree(mesh));
2895   PetscFunctionReturn(PETSC_SUCCESS);
2896 }
2897 
2898 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2899 {
2900   PetscSection           sectionGlobal, sectionLocal;
2901   PetscInt               bs = -1, mbs;
2902   PetscInt               localSize, localStart = 0;
2903   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2904   MatType                mtype;
2905   ISLocalToGlobalMapping ltog;
2906 
2907   PetscFunctionBegin;
2908   PetscCall(MatInitializePackage());
2909   mtype = dm->mattype;
2910   PetscCall(DMGetLocalSection(dm, &sectionLocal));
2911   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2912   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2913   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2914   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2915   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2916   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2917   PetscCall(MatSetType(*J, mtype));
2918   PetscCall(MatSetFromOptions(*J));
2919   PetscCall(MatGetBlockSize(*J, &mbs));
2920   if (mbs > 1) bs = mbs;
2921   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2922   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2923   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2924   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2925   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2926   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2927   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2928   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2929   if (!isShell) {
2930     // There are three states with pblocks, since block starts can have no dofs:
2931     // UNKNOWN) New Block:   An open block has been signalled by pblocks[p] == 1
2932     // TRUE)    Block Start: The first entry in a block has been added
2933     // FALSE)   Block Add:   An additional block entry has been added, since pblocks[p] == 0
2934     PetscBT         blst;
2935     PetscBool3      bstate     = PETSC_BOOL3_UNKNOWN;
2936     PetscBool       fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2937     const PetscInt *perm       = NULL;
2938     PetscInt       *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2939     PetscInt        pStart, pEnd, dof, cdof, num_fields;
2940 
2941     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2942     PetscCall(PetscSectionGetBlockStarts(sectionLocal, &blst));
2943     if (sectionLocal->perm) PetscCall(ISGetIndices(sectionLocal->perm, &perm));
2944 
2945     PetscCall(PetscCalloc1(localSize, &pblocks));
2946     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2947     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2948     // We need to process in the permuted order to get block sizes right
2949     for (PetscInt point = pStart; point < pEnd; ++point) {
2950       const PetscInt p = perm ? perm[point] : point;
2951 
2952       switch (dm->blocking_type) {
2953       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2954         PetscInt bdof, offset;
2955 
2956         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2957         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2958         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2959         if (blst && PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_UNKNOWN;
2960         if (dof > 0) {
2961           // State change
2962           if (bstate == PETSC_BOOL3_UNKNOWN) bstate = PETSC_BOOL3_TRUE;
2963           else if (bstate == PETSC_BOOL3_TRUE && blst && !PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_FALSE;
2964 
2965           for (PetscInt i = 0; i < dof - cdof; ++i) pblocks[offset - localStart + i] = dof - cdof;
2966           // Signal block concatenation
2967           if (bstate == PETSC_BOOL3_FALSE && dof - cdof) pblocks[offset - localStart] = -(dof - cdof);
2968         }
2969         dof  = dof < 0 ? -(dof + 1) : dof;
2970         bdof = cdof && (dof - cdof) ? 1 : dof;
2971         if (dof) {
2972           if (bs < 0) {
2973             bs = bdof;
2974           } else if (bs != bdof) {
2975             bs = 1;
2976           }
2977         }
2978       } break;
2979       case DM_BLOCKING_FIELD_NODE: {
2980         for (PetscInt field = 0; field < num_fields; field++) {
2981           PetscInt num_comp, bdof, offset;
2982           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2983           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2984           if (dof < 0) continue;
2985           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2986           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2987           PetscAssert(dof % num_comp == 0, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " field %" PetscInt_FMT " has %" PetscInt_FMT " dof, not divisible by %" PetscInt_FMT " component ", p, field, dof, num_comp);
2988           PetscInt num_nodes = dof / num_comp;
2989           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2990           // Handle possibly constant block size (unlikely)
2991           bdof = cdof && (dof - cdof) ? 1 : dof;
2992           if (dof) {
2993             if (bs < 0) {
2994               bs = bdof;
2995             } else if (bs != bdof) {
2996               bs = 1;
2997             }
2998           }
2999         }
3000       } break;
3001       }
3002     }
3003     if (sectionLocal->perm) PetscCall(ISRestoreIndices(sectionLocal->perm, &perm));
3004     /* Must have same blocksize on all procs (some might have no points) */
3005     bsLocal[0] = bs < 0 ? PETSC_INT_MAX : bs;
3006     bsLocal[1] = bs;
3007     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
3008     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
3009     else bs = bsMinMax[0];
3010     bs = PetscMax(1, bs);
3011     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
3012     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
3013       PetscCall(MatSetBlockSize(*J, bs));
3014       PetscCall(MatSetUp(*J));
3015     } else {
3016       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
3017       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
3018       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
3019     }
3020     if (pblocks) { // Consolidate blocks
3021       PetscInt nblocks = 0;
3022       pblocks[0]       = PetscAbs(pblocks[0]);
3023       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
3024         if (pblocks[i] == 0) continue;
3025         // Negative block size indicates the blocks should be concatenated
3026         if (pblocks[i] < 0) {
3027           pblocks[i] = -pblocks[i];
3028           pblocks[nblocks - 1] += pblocks[i];
3029         } else {
3030           pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
3031         }
3032         for (PetscInt j = 1; j < pblocks[i]; j++)
3033           PetscCheck(pblocks[i + j] == pblocks[i], PETSC_COMM_SELF, PETSC_ERR_PLIB, "Block of size %" PetscInt_FMT " at %" PetscInt_FMT " mismatches entry %" PetscInt_FMT " at %" PetscInt_FMT, pblocks[i], i, pblocks[i + j], i + j);
3034       }
3035       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
3036     }
3037     PetscCall(PetscFree(pblocks));
3038   }
3039   PetscCall(MatSetDM(*J, dm));
3040   PetscFunctionReturn(PETSC_SUCCESS);
3041 }
3042 
3043 /*@
3044   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
3045 
3046   Not Collective
3047 
3048   Input Parameter:
3049 . dm - The `DMPLEX`
3050 
3051   Output Parameter:
3052 . subsection - The subdomain section
3053 
3054   Level: developer
3055 
3056 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
3057 @*/
3058 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
3059 {
3060   DM_Plex *mesh = (DM_Plex *)dm->data;
3061 
3062   PetscFunctionBegin;
3063   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3064   if (!mesh->subdomainSection) {
3065     PetscSection section;
3066     PetscSF      sf;
3067 
3068     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
3069     PetscCall(DMGetLocalSection(dm, &section));
3070     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
3071     PetscCall(PetscSFDestroy(&sf));
3072   }
3073   *subsection = mesh->subdomainSection;
3074   PetscFunctionReturn(PETSC_SUCCESS);
3075 }
3076 
3077 /*@
3078   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
3079 
3080   Not Collective
3081 
3082   Input Parameter:
3083 . dm - The `DMPLEX`
3084 
3085   Output Parameters:
3086 + pStart - The first mesh point
3087 - pEnd   - The upper bound for mesh points
3088 
3089   Level: beginner
3090 
3091 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
3092 @*/
3093 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
3094 {
3095   DM_Plex *mesh = (DM_Plex *)dm->data;
3096 
3097   PetscFunctionBegin;
3098   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3099   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
3100   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
3101   PetscFunctionReturn(PETSC_SUCCESS);
3102 }
3103 
3104 /*@
3105   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
3106 
3107   Not Collective
3108 
3109   Input Parameters:
3110 + dm     - The `DMPLEX`
3111 . pStart - The first mesh point
3112 - pEnd   - The upper bound for mesh points
3113 
3114   Level: beginner
3115 
3116 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
3117 @*/
3118 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
3119 {
3120   DM_Plex *mesh = (DM_Plex *)dm->data;
3121 
3122   PetscFunctionBegin;
3123   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3124   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
3125   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
3126   PetscCall(PetscFree(mesh->cellTypes));
3127   PetscFunctionReturn(PETSC_SUCCESS);
3128 }
3129 
3130 /*@
3131   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
3132 
3133   Not Collective
3134 
3135   Input Parameters:
3136 + dm - The `DMPLEX`
3137 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3138 
3139   Output Parameter:
3140 . size - The cone size for point `p`
3141 
3142   Level: beginner
3143 
3144 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3145 @*/
3146 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
3147 {
3148   DM_Plex *mesh = (DM_Plex *)dm->data;
3149 
3150   PetscFunctionBegin;
3151   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3152   PetscAssertPointer(size, 3);
3153   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
3154   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
3155   PetscFunctionReturn(PETSC_SUCCESS);
3156 }
3157 
3158 /*@
3159   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
3160 
3161   Not Collective
3162 
3163   Input Parameters:
3164 + dm   - The `DMPLEX`
3165 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3166 - size - The cone size for point `p`
3167 
3168   Level: beginner
3169 
3170   Note:
3171   This should be called after `DMPlexSetChart()`.
3172 
3173 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
3174 @*/
3175 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
3176 {
3177   DM_Plex *mesh = (DM_Plex *)dm->data;
3178 
3179   PetscFunctionBegin;
3180   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3181   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
3182   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
3183   PetscFunctionReturn(PETSC_SUCCESS);
3184 }
3185 
3186 /*@C
3187   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
3188 
3189   Not Collective
3190 
3191   Input Parameters:
3192 + dm - The `DMPLEX`
3193 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3194 
3195   Output Parameter:
3196 . cone - An array of points which are on the in-edges for point `p`, the length of `cone` is the result of `DMPlexGetConeSize()`
3197 
3198   Level: beginner
3199 
3200   Fortran Notes:
3201   `cone` must be declared with
3202 .vb
3203   PetscInt, pointer :: cone(:)
3204 .ve
3205 
3206   You must call `DMPlexRestoreCone()` after you finish using the array.
3207   `DMPlexRestoreCone()` is not needed/available in C.
3208 
3209 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
3210 @*/
3211 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
3212 {
3213   DM_Plex *mesh = (DM_Plex *)dm->data;
3214   PetscInt off;
3215 
3216   PetscFunctionBegin;
3217   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3218   PetscAssertPointer(cone, 3);
3219   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3220   *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3221   PetscFunctionReturn(PETSC_SUCCESS);
3222 }
3223 
3224 /*@
3225   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
3226 
3227   Not Collective
3228 
3229   Input Parameters:
3230 + dm - The `DMPLEX`
3231 - p  - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3232 
3233   Output Parameters:
3234 + pConesSection - `PetscSection` describing the layout of `pCones`
3235 - pCones        - An `IS` containing the points which are on the in-edges for the point set `p`
3236 
3237   Level: intermediate
3238 
3239 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
3240 @*/
3241 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PeOp PetscSection *pConesSection, PeOp IS *pCones)
3242 {
3243   PetscSection cs, newcs;
3244   PetscInt    *cones;
3245   PetscInt    *newarr = NULL;
3246   PetscInt     n;
3247 
3248   PetscFunctionBegin;
3249   PetscCall(DMPlexGetCones(dm, &cones));
3250   PetscCall(DMPlexGetConeSection(dm, &cs));
3251   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
3252   if (pConesSection) *pConesSection = newcs;
3253   if (pCones) {
3254     PetscCall(PetscSectionGetStorageSize(newcs, &n));
3255     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
3256   }
3257   PetscFunctionReturn(PETSC_SUCCESS);
3258 }
3259 
3260 /*@
3261   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
3262 
3263   Not Collective
3264 
3265   Input Parameters:
3266 + dm     - The `DMPLEX`
3267 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3268 
3269   Output Parameter:
3270 . expandedPoints - An `IS` containing the of vertices recursively expanded from input points
3271 
3272   Level: advanced
3273 
3274   Notes:
3275   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
3276 
3277   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
3278 
3279 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
3280           `DMPlexGetDepth()`, `IS`
3281 @*/
3282 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
3283 {
3284   IS      *expandedPointsAll;
3285   PetscInt depth;
3286 
3287   PetscFunctionBegin;
3288   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3289   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3290   PetscAssertPointer(expandedPoints, 3);
3291   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3292   *expandedPoints = expandedPointsAll[0];
3293   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
3294   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3295   PetscFunctionReturn(PETSC_SUCCESS);
3296 }
3297 
3298 /*@
3299   DMPlexGetConeRecursive - Expand each given point into its cone points and do that recursively until we end up just with vertices
3300   (DAG points of depth 0, i.e., without cones).
3301 
3302   Not Collective
3303 
3304   Input Parameters:
3305 + dm     - The `DMPLEX`
3306 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3307 
3308   Output Parameters:
3309 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3310 . expandedPoints - (optional) An array of index sets with recursively expanded cones
3311 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3312 
3313   Level: advanced
3314 
3315   Notes:
3316   Like `DMPlexGetConeTuple()` but recursive.
3317 
3318   Array `expandedPoints` has size equal to `depth`. Each `expandedPoints`[d] contains DAG points with maximum depth d, recursively cone-wise expanded from the input points.
3319   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
3320 
3321   Array section has size equal to `depth`.  Each `PetscSection` `sections`[d] realizes mapping from `expandedPoints`[d+1] (section points) to `expandedPoints`[d] (section dofs) as follows\:
3322   (1) DAG points in `expandedPoints`[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
3323   (2) DAG points in `expandedPoints`[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
3324 
3325 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3326           `DMPlexGetDepth()`, `PetscSection`, `IS`
3327 @*/
3328 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PeOp PetscInt *depth, PeOp IS *expandedPoints[], PeOp PetscSection *sections[])
3329 {
3330   const PetscInt *arr0 = NULL, *cone = NULL;
3331   PetscInt       *arr = NULL, *newarr = NULL;
3332   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
3333   IS             *expandedPoints_;
3334   PetscSection   *sections_;
3335 
3336   PetscFunctionBegin;
3337   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3338   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3339   if (depth) PetscAssertPointer(depth, 3);
3340   if (expandedPoints) PetscAssertPointer(expandedPoints, 4);
3341   if (sections) PetscAssertPointer(sections, 5);
3342   PetscCall(ISGetLocalSize(points, &n));
3343   PetscCall(ISGetIndices(points, &arr0));
3344   PetscCall(DMPlexGetDepth(dm, &depth_));
3345   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
3346   PetscCall(PetscCalloc1(depth_, &sections_));
3347   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
3348   for (d = depth_ - 1; d >= 0; d--) {
3349     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
3350     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
3351     for (i = 0; i < n; i++) {
3352       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
3353       if (arr[i] >= start && arr[i] < end) {
3354         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
3355         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
3356       } else {
3357         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
3358       }
3359     }
3360     PetscCall(PetscSectionSetUp(sections_[d]));
3361     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
3362     PetscCall(PetscMalloc1(newn, &newarr));
3363     for (i = 0; i < n; i++) {
3364       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
3365       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
3366       if (cn > 1) {
3367         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
3368         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3369       } else {
3370         newarr[co] = arr[i];
3371       }
3372     }
3373     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3374     arr = newarr;
3375     n   = newn;
3376   }
3377   PetscCall(ISRestoreIndices(points, &arr0));
3378   *depth = depth_;
3379   if (expandedPoints) *expandedPoints = expandedPoints_;
3380   else {
3381     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3382     PetscCall(PetscFree(expandedPoints_));
3383   }
3384   if (sections) *sections = sections_;
3385   else {
3386     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3387     PetscCall(PetscFree(sections_));
3388   }
3389   PetscFunctionReturn(PETSC_SUCCESS);
3390 }
3391 
3392 /*@
3393   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3394 
3395   Not Collective
3396 
3397   Input Parameters:
3398 + dm     - The `DMPLEX`
3399 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3400 
3401   Output Parameters:
3402 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3403 . expandedPoints - (optional) An array of recursively expanded cones
3404 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3405 
3406   Level: advanced
3407 
3408   Note:
3409   See `DMPlexGetConeRecursive()`
3410 
3411 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3412           `DMPlexGetDepth()`, `IS`, `PetscSection`
3413 @*/
3414 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PeOp PetscInt *depth, PeOp IS *expandedPoints[], PeOp PetscSection *sections[])
3415 {
3416   PetscInt d, depth_;
3417 
3418   PetscFunctionBegin;
3419   PetscCall(DMPlexGetDepth(dm, &depth_));
3420   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3421   if (depth) *depth = 0;
3422   if (expandedPoints) {
3423     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&(*expandedPoints)[d]));
3424     PetscCall(PetscFree(*expandedPoints));
3425   }
3426   if (sections) {
3427     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&(*sections)[d]));
3428     PetscCall(PetscFree(*sections));
3429   }
3430   PetscFunctionReturn(PETSC_SUCCESS);
3431 }
3432 
3433 /*@
3434   DMPlexSetCone - Set the points on the in-edges for this point in the DAG; that is these are the points that cover the specific point
3435 
3436   Not Collective
3437 
3438   Input Parameters:
3439 + dm   - The `DMPLEX`
3440 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3441 - cone - An array of points which are on the in-edges for point `p`, its length must have been previously provided with `DMPlexSetConeSize()`
3442 
3443   Level: beginner
3444 
3445   Note:
3446   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3447 
3448 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3449 @*/
3450 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3451 {
3452   DM_Plex *mesh = (DM_Plex *)dm->data;
3453   PetscInt dof, off, c;
3454 
3455   PetscFunctionBegin;
3456   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3457   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3458   if (dof) PetscAssertPointer(cone, 3);
3459   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3460   if (PetscDefined(USE_DEBUG)) {
3461     PetscInt pStart, pEnd;
3462     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3463     PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3464     for (c = 0; c < dof; ++c) {
3465       PetscCheck(!(cone[c] < pStart) && !(cone[c] >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", cone[c], pStart, pEnd);
3466       mesh->cones[off + c] = cone[c];
3467     }
3468   } else {
3469     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3470   }
3471   PetscFunctionReturn(PETSC_SUCCESS);
3472 }
3473 
3474 /*@C
3475   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3476 
3477   Not Collective
3478 
3479   Input Parameters:
3480 + dm - The `DMPLEX`
3481 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3482 
3483   Output Parameter:
3484 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3485                     integer giving the prescription for cone traversal. Its length is given by the result of `DMPlexSetConeSize()`
3486 
3487   Level: beginner
3488 
3489   Note:
3490   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3491   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3492   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3493   with the identity.
3494 
3495   Fortran Notes:
3496   You must call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3497   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3498 
3499 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetConeSize()`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`,
3500           `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3501 @*/
3502 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3503 {
3504   DM_Plex *mesh = (DM_Plex *)dm->data;
3505   PetscInt off;
3506 
3507   PetscFunctionBegin;
3508   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3509   if (PetscDefined(USE_DEBUG)) {
3510     PetscInt dof;
3511     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3512     if (dof) PetscAssertPointer(coneOrientation, 3);
3513   }
3514   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3515 
3516   *coneOrientation = &mesh->coneOrientations[off];
3517   PetscFunctionReturn(PETSC_SUCCESS);
3518 }
3519 
3520 /*@
3521   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3522 
3523   Not Collective
3524 
3525   Input Parameters:
3526 + dm              - The `DMPLEX`
3527 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3528 - coneOrientation - An array of orientations. Its length is given by the result of `DMPlexSetConeSize()`
3529 
3530   Level: beginner
3531 
3532   Notes:
3533   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3534 
3535   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3536 
3537 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3538 @*/
3539 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3540 {
3541   DM_Plex *mesh = (DM_Plex *)dm->data;
3542   PetscInt pStart, pEnd;
3543   PetscInt dof, off, c;
3544 
3545   PetscFunctionBegin;
3546   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3547   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3548   if (dof) PetscAssertPointer(coneOrientation, 3);
3549   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3550   if (PetscDefined(USE_DEBUG)) {
3551     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3552     PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3553     for (c = 0; c < dof; ++c) {
3554       PetscInt cdof, o = coneOrientation[c];
3555 
3556       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3557       PetscCheck(!o || (o >= -(cdof + 1) && o < cdof), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone orientation %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ". %" PetscInt_FMT ")", o, -(cdof + 1), cdof);
3558       mesh->coneOrientations[off + c] = o;
3559     }
3560   } else {
3561     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3562   }
3563   PetscFunctionReturn(PETSC_SUCCESS);
3564 }
3565 
3566 /*@
3567   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3568 
3569   Not Collective
3570 
3571   Input Parameters:
3572 + dm        - The `DMPLEX`
3573 . p         - The point, which must lie in the chart set with `DMPlexSetChart()`
3574 . conePos   - The local index in the cone where the point should be put
3575 - conePoint - The mesh point to insert
3576 
3577   Level: beginner
3578 
3579 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3580 @*/
3581 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3582 {
3583   DM_Plex *mesh = (DM_Plex *)dm->data;
3584   PetscInt pStart, pEnd;
3585   PetscInt dof, off;
3586 
3587   PetscFunctionBegin;
3588   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3589   if (PetscDefined(USE_DEBUG)) {
3590     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3591     PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3592     PetscCheck(!(conePoint < pStart) && !(conePoint >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", conePoint, pStart, pEnd);
3593     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3594     PetscCheck(!(conePos < 0) && !(conePos >= dof), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone position %" PetscInt_FMT " of point %" PetscInt_FMT " is not in the valid range [0, %" PetscInt_FMT ")", conePos, p, dof);
3595   }
3596   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3597   mesh->cones[off + conePos] = conePoint;
3598   PetscFunctionReturn(PETSC_SUCCESS);
3599 }
3600 
3601 /*@
3602   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3603 
3604   Not Collective
3605 
3606   Input Parameters:
3607 + dm              - The `DMPLEX`
3608 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3609 . conePos         - The local index in the cone where the point should be put
3610 - coneOrientation - The point orientation to insert
3611 
3612   Level: beginner
3613 
3614   Note:
3615   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3616 
3617 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3618 @*/
3619 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3620 {
3621   DM_Plex *mesh = (DM_Plex *)dm->data;
3622   PetscInt pStart, pEnd;
3623   PetscInt dof, off;
3624 
3625   PetscFunctionBegin;
3626   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3627   if (PetscDefined(USE_DEBUG)) {
3628     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3629     PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3630     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3631     PetscCheck(!(conePos < 0) && !(conePos >= dof), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone position %" PetscInt_FMT " of point %" PetscInt_FMT " is not in the valid range [0, %" PetscInt_FMT ")", conePos, p, dof);
3632   }
3633   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3634   mesh->coneOrientations[off + conePos] = coneOrientation;
3635   PetscFunctionReturn(PETSC_SUCCESS);
3636 }
3637 
3638 /*@C
3639   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3640 
3641   Not collective
3642 
3643   Input Parameters:
3644 + dm - The DMPlex
3645 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3646 
3647   Output Parameters:
3648 + cone - An array of points which are on the in-edges for point `p`
3649 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3650          integer giving the prescription for cone traversal.
3651 
3652   Level: beginner
3653 
3654   Notes:
3655   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3656   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3657   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3658   with the identity.
3659 
3660   You must also call `DMPlexRestoreOrientedCone()` after you finish using the returned array.
3661 
3662   Fortran Notes:
3663   `cone` and `ornt` must be declared with
3664 .vb
3665   PetscInt, pointer :: cone(:)
3666   PetscInt, pointer :: ornt(:)
3667 .ve
3668 
3669 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3670 @*/
3671 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, PeOp const PetscInt *cone[], PeOp const PetscInt *ornt[])
3672 {
3673   DM_Plex *mesh = (DM_Plex *)dm->data;
3674 
3675   PetscFunctionBegin;
3676   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3677   if (mesh->tr) {
3678     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3679   } else {
3680     PetscInt off;
3681     if (PetscDefined(USE_DEBUG)) {
3682       PetscInt dof;
3683       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3684       if (dof) {
3685         if (cone) PetscAssertPointer(cone, 3);
3686         if (ornt) PetscAssertPointer(ornt, 4);
3687       }
3688     }
3689     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3690     if (cone) *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3691     if (ornt) *ornt = PetscSafePointerPlusOffset(mesh->coneOrientations, off);
3692   }
3693   PetscFunctionReturn(PETSC_SUCCESS);
3694 }
3695 
3696 /*@C
3697   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG obtained with `DMPlexGetOrientedCone()`
3698 
3699   Not Collective
3700 
3701   Input Parameters:
3702 + dm   - The DMPlex
3703 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3704 . cone - An array of points which are on the in-edges for point p
3705 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3706          integer giving the prescription for cone traversal.
3707 
3708   Level: beginner
3709 
3710 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3711 @*/
3712 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3713 {
3714   DM_Plex *mesh = (DM_Plex *)dm->data;
3715 
3716   PetscFunctionBegin;
3717   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3718   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3719   PetscFunctionReturn(PETSC_SUCCESS);
3720 }
3721 
3722 /*@
3723   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3724 
3725   Not Collective
3726 
3727   Input Parameters:
3728 + dm - The `DMPLEX`
3729 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3730 
3731   Output Parameter:
3732 . size - The support size for point `p`
3733 
3734   Level: beginner
3735 
3736 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3737 @*/
3738 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3739 {
3740   DM_Plex *mesh = (DM_Plex *)dm->data;
3741 
3742   PetscFunctionBegin;
3743   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3744   PetscAssertPointer(size, 3);
3745   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3746   PetscFunctionReturn(PETSC_SUCCESS);
3747 }
3748 
3749 /*@
3750   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3751 
3752   Not Collective
3753 
3754   Input Parameters:
3755 + dm   - The `DMPLEX`
3756 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3757 - size - The support size for point `p`
3758 
3759   Level: beginner
3760 
3761   Note:
3762   This should be called after `DMPlexSetChart()`.
3763 
3764 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3765 @*/
3766 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3767 {
3768   DM_Plex *mesh = (DM_Plex *)dm->data;
3769 
3770   PetscFunctionBegin;
3771   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3772   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3773   PetscFunctionReturn(PETSC_SUCCESS);
3774 }
3775 
3776 /*@C
3777   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3778 
3779   Not Collective
3780 
3781   Input Parameters:
3782 + dm - The `DMPLEX`
3783 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3784 
3785   Output Parameter:
3786 . support - An array of points which are on the out-edges for point `p`, its length is that obtained from `DMPlexGetSupportSize()`
3787 
3788   Level: beginner
3789 
3790   Fortran Notes:
3791   `support` must be declared with
3792 .vb
3793   PetscInt, pointer :: support(:)
3794 .ve
3795 
3796   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3797   `DMPlexRestoreSupport()` is not needed/available in C.
3798 
3799 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3800 @*/
3801 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3802 {
3803   DM_Plex *mesh = (DM_Plex *)dm->data;
3804   PetscInt off;
3805 
3806   PetscFunctionBegin;
3807   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3808   PetscAssertPointer(support, 3);
3809   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3810   *support = PetscSafePointerPlusOffset(mesh->supports, off);
3811   PetscFunctionReturn(PETSC_SUCCESS);
3812 }
3813 
3814 /*@
3815   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3816 
3817   Not Collective
3818 
3819   Input Parameters:
3820 + dm      - The `DMPLEX`
3821 . p       - The point, which must lie in the chart set with `DMPlexSetChart()`
3822 - support - An array of points which are on the out-edges for point `p`, its length is that obtained from `DMPlexGetSupportSize()`
3823 
3824   Level: beginner
3825 
3826   Note:
3827   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3828 
3829 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3830 @*/
3831 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3832 {
3833   DM_Plex *mesh = (DM_Plex *)dm->data;
3834   PetscInt pStart, pEnd;
3835   PetscInt dof, off, c;
3836 
3837   PetscFunctionBegin;
3838   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3839   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3840   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3841   if (dof) PetscAssertPointer(support, 3);
3842   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3843   PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3844   for (c = 0; c < dof; ++c) {
3845     PetscCheck(!(support[c] < pStart) && !(support[c] >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Support point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", support[c], pStart, pEnd);
3846     mesh->supports[off + c] = support[c];
3847   }
3848   PetscFunctionReturn(PETSC_SUCCESS);
3849 }
3850 
3851 /*@
3852   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3853 
3854   Not Collective
3855 
3856   Input Parameters:
3857 + dm           - The `DMPLEX`
3858 . p            - The point, which must lie in the chart set with `DMPlexSetChart()`
3859 . supportPos   - The local index in the cone where the point should be put
3860 - supportPoint - The mesh point to insert
3861 
3862   Level: beginner
3863 
3864 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3865 @*/
3866 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3867 {
3868   DM_Plex *mesh = (DM_Plex *)dm->data;
3869   PetscInt pStart, pEnd;
3870   PetscInt dof, off;
3871 
3872   PetscFunctionBegin;
3873   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3874   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3875   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3876   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3877   PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3878   PetscCheck(!(supportPoint < pStart) && !(supportPoint >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Support point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", supportPoint, pStart, pEnd);
3879   PetscCheck(supportPos < dof, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Support position %" PetscInt_FMT " of point %" PetscInt_FMT " is not in the valid range [0, %" PetscInt_FMT ")", supportPos, p, dof);
3880   mesh->supports[off + supportPos] = supportPoint;
3881   PetscFunctionReturn(PETSC_SUCCESS);
3882 }
3883 
3884 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3885 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3886 {
3887   switch (ct) {
3888   case DM_POLYTOPE_SEGMENT:
3889     if (o == -1) return -2;
3890     break;
3891   case DM_POLYTOPE_TRIANGLE:
3892     if (o == -3) return -1;
3893     if (o == -2) return -3;
3894     if (o == -1) return -2;
3895     break;
3896   case DM_POLYTOPE_QUADRILATERAL:
3897     if (o == -4) return -2;
3898     if (o == -3) return -1;
3899     if (o == -2) return -4;
3900     if (o == -1) return -3;
3901     break;
3902   default:
3903     return o;
3904   }
3905   return o;
3906 }
3907 
3908 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3909 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3910 {
3911   switch (ct) {
3912   case DM_POLYTOPE_SEGMENT:
3913     if ((o == -2) || (o == 1)) return -1;
3914     if (o == -1) return 0;
3915     break;
3916   case DM_POLYTOPE_TRIANGLE:
3917     if (o == -3) return -2;
3918     if (o == -2) return -1;
3919     if (o == -1) return -3;
3920     break;
3921   case DM_POLYTOPE_QUADRILATERAL:
3922     if (o == -4) return -2;
3923     if (o == -3) return -1;
3924     if (o == -2) return -4;
3925     if (o == -1) return -3;
3926     break;
3927   default:
3928     return o;
3929   }
3930   return o;
3931 }
3932 
3933 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3934 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3935 {
3936   PetscInt pStart, pEnd, p;
3937 
3938   PetscFunctionBegin;
3939   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3940   for (p = pStart; p < pEnd; ++p) {
3941     const PetscInt *cone, *ornt;
3942     PetscInt        coneSize, c;
3943 
3944     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3945     PetscCall(DMPlexGetCone(dm, p, &cone));
3946     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3947     for (c = 0; c < coneSize; ++c) {
3948       DMPolytopeType ct;
3949       const PetscInt o = ornt[c];
3950 
3951       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3952       switch (ct) {
3953       case DM_POLYTOPE_SEGMENT:
3954         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3955         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3956         break;
3957       case DM_POLYTOPE_TRIANGLE:
3958         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3959         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3960         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3961         break;
3962       case DM_POLYTOPE_QUADRILATERAL:
3963         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3964         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3965         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3966         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3967         break;
3968       default:
3969         break;
3970       }
3971     }
3972   }
3973   PetscFunctionReturn(PETSC_SUCCESS);
3974 }
3975 
3976 static inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3977 {
3978   DM_Plex *mesh = (DM_Plex *)dm->data;
3979 
3980   PetscFunctionBeginHot;
3981   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3982     if (useCone) {
3983       PetscCall(DMPlexGetConeSize(dm, p, size));
3984       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3985     } else {
3986       PetscCall(DMPlexGetSupportSize(dm, p, size));
3987       PetscCall(DMPlexGetSupport(dm, p, arr));
3988     }
3989   } else {
3990     if (useCone) {
3991       const PetscSection s   = mesh->coneSection;
3992       const PetscInt     ps  = p - s->pStart;
3993       const PetscInt     off = s->atlasOff[ps];
3994 
3995       *size = s->atlasDof[ps];
3996       *arr  = mesh->cones + off;
3997       *ornt = mesh->coneOrientations + off;
3998     } else {
3999       const PetscSection s   = mesh->supportSection;
4000       const PetscInt     ps  = p - s->pStart;
4001       const PetscInt     off = s->atlasOff[ps];
4002 
4003       *size = s->atlasDof[ps];
4004       *arr  = mesh->supports + off;
4005     }
4006   }
4007   PetscFunctionReturn(PETSC_SUCCESS);
4008 }
4009 
4010 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
4011 {
4012   DM_Plex *mesh = (DM_Plex *)dm->data;
4013 
4014   PetscFunctionBeginHot;
4015   if (PetscDefined(USE_DEBUG) || mesh->tr) {
4016     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
4017   }
4018   PetscFunctionReturn(PETSC_SUCCESS);
4019 }
4020 
4021 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4022 {
4023   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
4024   PetscInt       *closure;
4025   const PetscInt *tmp = NULL, *tmpO = NULL;
4026   PetscInt        off = 0, tmpSize, t;
4027 
4028   PetscFunctionBeginHot;
4029   if (ornt) {
4030     PetscCall(DMPlexGetCellType(dm, p, &ct));
4031     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN || ct == DM_POLYTOPE_UNKNOWN_CELL || ct == DM_POLYTOPE_UNKNOWN_FACE) ct = DM_POLYTOPE_UNKNOWN;
4032   }
4033   if (*points) {
4034     closure = *points;
4035   } else {
4036     PetscInt maxConeSize, maxSupportSize;
4037     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
4038     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
4039   }
4040   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
4041   if (ct == DM_POLYTOPE_UNKNOWN) {
4042     closure[off++] = p;
4043     closure[off++] = 0;
4044     for (t = 0; t < tmpSize; ++t) {
4045       closure[off++] = tmp[t];
4046       closure[off++] = tmpO ? tmpO[t] : 0;
4047     }
4048   } else {
4049     const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, ornt);
4050 
4051     /* We assume that cells with a valid type have faces with a valid type */
4052     closure[off++] = p;
4053     closure[off++] = ornt;
4054     for (t = 0; t < tmpSize; ++t) {
4055       DMPolytopeType ft;
4056 
4057       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
4058       closure[off++] = tmp[arr[t]];
4059       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
4060     }
4061   }
4062   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
4063   if (numPoints) *numPoints = tmpSize + 1;
4064   if (points) *points = closure;
4065   PetscFunctionReturn(PETSC_SUCCESS);
4066 }
4067 
4068 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
4069 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
4070 {
4071   const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, o);
4072   const PetscInt *cone, *ornt;
4073   PetscInt       *pts, *closure = NULL;
4074   DMPolytopeType  ft;
4075   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
4076   PetscInt        dim, coneSize, c, d, clSize, cl;
4077 
4078   PetscFunctionBeginHot;
4079   PetscCall(DMGetDimension(dm, &dim));
4080   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
4081   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
4082   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
4083   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
4084   maxSize       = PetscMax(coneSeries, supportSeries);
4085   if (*points) {
4086     pts = *points;
4087   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
4088   c        = 0;
4089   pts[c++] = point;
4090   pts[c++] = o;
4091   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
4092   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
4093   for (cl = 0; cl < clSize * 2; cl += 2) {
4094     pts[c++] = closure[cl];
4095     pts[c++] = closure[cl + 1];
4096   }
4097   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
4098   for (cl = 0; cl < clSize * 2; cl += 2) {
4099     pts[c++] = closure[cl];
4100     pts[c++] = closure[cl + 1];
4101   }
4102   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
4103   for (d = 2; d < coneSize; ++d) {
4104     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
4105     pts[c++] = cone[arr[d * 2 + 0]];
4106     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
4107   }
4108   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
4109   if (dim >= 3) {
4110     for (d = 2; d < coneSize; ++d) {
4111       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
4112       const PetscInt *fcone, *fornt;
4113       PetscInt        fconeSize, fc, i;
4114 
4115       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
4116       const PetscInt *farr = DMPolytopeTypeGetArrangement(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
4117       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
4118       for (fc = 0; fc < fconeSize; ++fc) {
4119         const PetscInt cp = fcone[farr[fc * 2 + 0]];
4120         const PetscInt co = farr[fc * 2 + 1];
4121 
4122         for (i = 0; i < c; i += 2)
4123           if (pts[i] == cp) break;
4124         if (i == c) {
4125           PetscCall(DMPlexGetCellType(dm, cp, &ft));
4126           pts[c++] = cp;
4127           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
4128         }
4129       }
4130       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
4131     }
4132   }
4133   *numPoints = c / 2;
4134   *points    = pts;
4135   PetscFunctionReturn(PETSC_SUCCESS);
4136 }
4137 
4138 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4139 {
4140   DMPolytopeType ct;
4141   PetscInt      *closure, *fifo;
4142   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
4143   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
4144   PetscInt       depth, maxSize;
4145 
4146   PetscFunctionBeginHot;
4147   PetscCall(DMPlexGetDepth(dm, &depth));
4148   if (depth == 1) {
4149     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
4150     PetscFunctionReturn(PETSC_SUCCESS);
4151   }
4152   PetscCall(DMPlexGetCellType(dm, p, &ct));
4153   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN || ct == DM_POLYTOPE_UNKNOWN_CELL || ct == DM_POLYTOPE_UNKNOWN_FACE) ct = DM_POLYTOPE_UNKNOWN;
4154   if (DMPolytopeTypeIsHybrid(ct) && ct != DM_POLYTOPE_POINT_PRISM_TENSOR) {
4155     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
4156     PetscFunctionReturn(PETSC_SUCCESS);
4157   }
4158   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
4159   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
4160   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
4161   maxSize       = PetscMax(coneSeries, supportSeries);
4162   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4163   if (*points) {
4164     closure = *points;
4165   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
4166   closure[closureSize++] = p;
4167   closure[closureSize++] = ornt;
4168   fifo[fifoSize++]       = p;
4169   fifo[fifoSize++]       = ornt;
4170   fifo[fifoSize++]       = ct;
4171   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
4172   while (fifoSize - fifoStart) {
4173     const PetscInt       q    = fifo[fifoStart++];
4174     const PetscInt       o    = fifo[fifoStart++];
4175     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
4176     const PetscInt      *qarr = DMPolytopeTypeGetArrangement(qt, o);
4177     const PetscInt      *tmp, *tmpO = NULL;
4178     PetscInt             tmpSize, t;
4179 
4180     if (PetscDefined(USE_DEBUG)) {
4181       PetscInt nO = DMPolytopeTypeGetNumArrangements(qt) / 2;
4182       PetscCheck(!o || !(o >= nO || o < -nO), PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid orientation %" PetscInt_FMT " not in [%" PetscInt_FMT ",%" PetscInt_FMT ") for %s %" PetscInt_FMT, o, -nO, nO, DMPolytopeTypes[qt], q);
4183     }
4184     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4185     for (t = 0; t < tmpSize; ++t) {
4186       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
4187       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
4188       const PetscInt cp = tmp[ip];
4189       PetscCall(DMPlexGetCellType(dm, cp, &ct));
4190       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
4191       PetscInt       c;
4192 
4193       /* Check for duplicate */
4194       for (c = 0; c < closureSize; c += 2) {
4195         if (closure[c] == cp) break;
4196       }
4197       if (c == closureSize) {
4198         closure[closureSize++] = cp;
4199         closure[closureSize++] = co;
4200         fifo[fifoSize++]       = cp;
4201         fifo[fifoSize++]       = co;
4202         fifo[fifoSize++]       = ct;
4203       }
4204     }
4205     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4206   }
4207   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4208   if (numPoints) *numPoints = closureSize / 2;
4209   if (points) *points = closure;
4210   PetscFunctionReturn(PETSC_SUCCESS);
4211 }
4212 
4213 /*@C
4214   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
4215 
4216   Not Collective
4217 
4218   Input Parameters:
4219 + dm      - The `DMPLEX`
4220 . p       - The mesh point
4221 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
4222 
4223   Input/Output Parameter:
4224 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
4225            if *points is `NULL` on input, internal storage will be returned, use `DMPlexRestoreTransitiveClosure()`,
4226            otherwise the provided array is used to hold the values
4227 
4228   Output Parameter:
4229 . numPoints - The number of points in the closure, so `points` is of size 2*`numPoints`
4230 
4231   Level: beginner
4232 
4233   Note:
4234   If using internal storage (points is `NULL` on input), each call overwrites the last output.
4235 
4236   Fortran Notes:
4237   `points` must be declared with
4238 .vb
4239   PetscInt, pointer :: points(:)
4240 .ve
4241   and is always allocated by the function.
4242 
4243   Pass `PETSC_NULL_INTEGER` for `numPoints` if it is not needed
4244 
4245 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4246 @*/
4247 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4248 {
4249   PetscFunctionBeginHot;
4250   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4251   if (numPoints) PetscAssertPointer(numPoints, 4);
4252   if (points) PetscAssertPointer(points, 5);
4253   if (PetscDefined(USE_DEBUG)) {
4254     PetscInt pStart, pEnd;
4255     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4256     PetscCheck(p >= pStart && p < pEnd, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Point %" PetscInt_FMT " is not in [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
4257   }
4258   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
4259   PetscFunctionReturn(PETSC_SUCCESS);
4260 }
4261 
4262 /*@C
4263   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
4264 
4265   Not Collective
4266 
4267   Input Parameters:
4268 + dm        - The `DMPLEX`
4269 . p         - The mesh point
4270 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
4271 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4272 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
4273 
4274   Level: beginner
4275 
4276   Note:
4277   If not using internal storage (points is not `NULL` on input), this call is unnecessary
4278 
4279 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4280 @*/
4281 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4282 {
4283   PetscFunctionBeginHot;
4284   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4285   if (numPoints) *numPoints = 0;
4286   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
4287   PetscFunctionReturn(PETSC_SUCCESS);
4288 }
4289 
4290 /*@
4291   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
4292 
4293   Not Collective
4294 
4295   Input Parameter:
4296 . dm - The `DMPLEX`
4297 
4298   Output Parameters:
4299 + maxConeSize    - The maximum number of in-edges
4300 - maxSupportSize - The maximum number of out-edges
4301 
4302   Level: beginner
4303 
4304 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
4305 @*/
4306 PetscErrorCode DMPlexGetMaxSizes(DM dm, PeOp PetscInt *maxConeSize, PeOp PetscInt *maxSupportSize)
4307 {
4308   DM_Plex *mesh = (DM_Plex *)dm->data;
4309 
4310   PetscFunctionBegin;
4311   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4312   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
4313   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
4314   PetscFunctionReturn(PETSC_SUCCESS);
4315 }
4316 
4317 PetscErrorCode DMSetUp_Plex(DM dm)
4318 {
4319   DM_Plex *mesh = (DM_Plex *)dm->data;
4320   PetscInt size, maxSupportSize;
4321 
4322   PetscFunctionBegin;
4323   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4324   PetscCall(PetscSectionSetUp(mesh->coneSection));
4325   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
4326   PetscCall(PetscMalloc1(size, &mesh->cones));
4327   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
4328   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4329   if (maxSupportSize) {
4330     PetscCall(PetscSectionSetUp(mesh->supportSection));
4331     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
4332     PetscCall(PetscMalloc1(size, &mesh->supports));
4333   }
4334   PetscFunctionReturn(PETSC_SUCCESS);
4335 }
4336 
4337 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
4338 {
4339   PetscFunctionBegin;
4340   if (subdm) PetscCall(DMClone(dm, subdm));
4341   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, NULL, NULL, is, subdm));
4342   if (subdm) (*subdm)->useNatural = dm->useNatural;
4343   if (dm->useNatural && dm->sfMigration) {
4344     PetscSF sfNatural;
4345 
4346     (*subdm)->sfMigration = dm->sfMigration;
4347     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
4348     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
4349     (*subdm)->sfNatural = sfNatural;
4350   }
4351   PetscFunctionReturn(PETSC_SUCCESS);
4352 }
4353 
4354 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
4355 {
4356   PetscInt i = 0;
4357 
4358   PetscFunctionBegin;
4359   PetscCall(DMClone(dms[0], superdm));
4360   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
4361   (*superdm)->useNatural = PETSC_FALSE;
4362   for (i = 0; i < len; i++) {
4363     if (dms[i]->useNatural && dms[i]->sfMigration) {
4364       PetscSF sfNatural;
4365 
4366       (*superdm)->sfMigration = dms[i]->sfMigration;
4367       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
4368       (*superdm)->useNatural = PETSC_TRUE;
4369       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
4370       (*superdm)->sfNatural = sfNatural;
4371       break;
4372     }
4373   }
4374   PetscFunctionReturn(PETSC_SUCCESS);
4375 }
4376 
4377 /*@
4378   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
4379 
4380   Not Collective
4381 
4382   Input Parameter:
4383 . dm - The `DMPLEX`
4384 
4385   Level: beginner
4386 
4387   Note:
4388   This should be called after all calls to `DMPlexSetCone()`
4389 
4390 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4391 @*/
4392 PetscErrorCode DMPlexSymmetrize(DM dm)
4393 {
4394   DM_Plex  *mesh = (DM_Plex *)dm->data;
4395   PetscInt *offsets;
4396   PetscInt  supportSize;
4397   PetscInt  pStart, pEnd, p;
4398 
4399   PetscFunctionBegin;
4400   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4401   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4402   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4403   /* Calculate support sizes */
4404   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4405   for (p = pStart; p < pEnd; ++p) {
4406     PetscInt dof, off, c;
4407 
4408     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4409     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4410     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4411   }
4412   PetscCall(PetscSectionSetUp(mesh->supportSection));
4413   /* Calculate supports */
4414   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4415   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4416   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4417   for (p = pStart; p < pEnd; ++p) {
4418     PetscInt dof, off, c;
4419 
4420     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4421     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4422     for (c = off; c < off + dof; ++c) {
4423       const PetscInt q = mesh->cones[c];
4424       PetscInt       offS;
4425 
4426       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4427 
4428       mesh->supports[offS + offsets[q]] = p;
4429       ++offsets[q];
4430     }
4431   }
4432   PetscCall(PetscFree(offsets));
4433   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4434   PetscFunctionReturn(PETSC_SUCCESS);
4435 }
4436 
4437 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4438 {
4439   IS stratumIS;
4440 
4441   PetscFunctionBegin;
4442   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4443   if (PetscDefined(USE_DEBUG)) {
4444     PetscInt  qStart, qEnd, numLevels, level;
4445     PetscBool overlap = PETSC_FALSE;
4446     PetscCall(DMLabelGetNumValues(label, &numLevels));
4447     for (level = 0; level < numLevels; level++) {
4448       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4449       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4450         overlap = PETSC_TRUE;
4451         break;
4452       }
4453     }
4454     PetscCheck(!overlap, PETSC_COMM_SELF, PETSC_ERR_PLIB, "New depth %" PetscInt_FMT " range [%" PetscInt_FMT ",%" PetscInt_FMT ") overlaps with depth %" PetscInt_FMT " range [%" PetscInt_FMT ",%" PetscInt_FMT ")", depth, pStart, pEnd, level, qStart, qEnd);
4455   }
4456   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4457   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4458   PetscCall(ISDestroy(&stratumIS));
4459   PetscFunctionReturn(PETSC_SUCCESS);
4460 }
4461 
4462 static PetscErrorCode DMPlexStratify_CellType_Private(DM dm, DMLabel label)
4463 {
4464   PetscInt *pMin, *pMax;
4465   PetscInt  pStart, pEnd;
4466   PetscInt  dmin = PETSC_INT_MAX, dmax = PETSC_INT_MIN;
4467 
4468   PetscFunctionBegin;
4469   {
4470     DMLabel label2;
4471 
4472     PetscCall(DMPlexGetCellTypeLabel(dm, &label2));
4473     PetscCall(PetscObjectViewFromOptions((PetscObject)label2, NULL, "-ct_view"));
4474   }
4475   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4476   for (PetscInt p = pStart; p < pEnd; ++p) {
4477     DMPolytopeType ct;
4478 
4479     PetscCall(DMPlexGetCellType(dm, p, &ct));
4480     dmin = PetscMin(DMPolytopeTypeGetDim(ct), dmin);
4481     dmax = PetscMax(DMPolytopeTypeGetDim(ct), dmax);
4482   }
4483   PetscCall(PetscMalloc2(dmax + 1, &pMin, dmax + 1, &pMax));
4484   for (PetscInt d = dmin; d <= dmax; ++d) {
4485     pMin[d] = PETSC_INT_MAX;
4486     pMax[d] = PETSC_INT_MIN;
4487   }
4488   for (PetscInt p = pStart; p < pEnd; ++p) {
4489     DMPolytopeType ct;
4490     PetscInt       d;
4491 
4492     PetscCall(DMPlexGetCellType(dm, p, &ct));
4493     d       = DMPolytopeTypeGetDim(ct);
4494     pMin[d] = PetscMin(p, pMin[d]);
4495     pMax[d] = PetscMax(p, pMax[d]);
4496   }
4497   for (PetscInt d = dmin; d <= dmax; ++d) {
4498     if (pMin[d] > pMax[d]) continue;
4499     PetscCall(DMPlexCreateDepthStratum(dm, label, d, pMin[d], pMax[d] + 1));
4500   }
4501   PetscCall(PetscFree2(pMin, pMax));
4502   PetscFunctionReturn(PETSC_SUCCESS);
4503 }
4504 
4505 static PetscErrorCode DMPlexStratify_Topological_Private(DM dm, DMLabel label)
4506 {
4507   PetscInt pStart, pEnd;
4508   PetscInt numRoots = 0, numLeaves = 0;
4509 
4510   PetscFunctionBegin;
4511   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4512   {
4513     /* Initialize roots and count leaves */
4514     PetscInt sMin = PETSC_INT_MAX;
4515     PetscInt sMax = PETSC_INT_MIN;
4516     PetscInt coneSize, supportSize;
4517 
4518     for (PetscInt p = pStart; p < pEnd; ++p) {
4519       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4520       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4521       if (!coneSize && supportSize) {
4522         sMin = PetscMin(p, sMin);
4523         sMax = PetscMax(p, sMax);
4524         ++numRoots;
4525       } else if (!supportSize && coneSize) {
4526         ++numLeaves;
4527       } else if (!supportSize && !coneSize) {
4528         /* Isolated points */
4529         sMin = PetscMin(p, sMin);
4530         sMax = PetscMax(p, sMax);
4531       }
4532     }
4533     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4534   }
4535 
4536   if (numRoots + numLeaves == (pEnd - pStart)) {
4537     PetscInt sMin = PETSC_INT_MAX;
4538     PetscInt sMax = PETSC_INT_MIN;
4539     PetscInt coneSize, supportSize;
4540 
4541     for (PetscInt p = pStart; p < pEnd; ++p) {
4542       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4543       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4544       if (!supportSize && coneSize) {
4545         sMin = PetscMin(p, sMin);
4546         sMax = PetscMax(p, sMax);
4547       }
4548     }
4549     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4550   } else {
4551     PetscInt level = 0;
4552     PetscInt qStart, qEnd;
4553 
4554     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4555     while (qEnd > qStart) {
4556       PetscInt sMin = PETSC_INT_MAX;
4557       PetscInt sMax = PETSC_INT_MIN;
4558 
4559       for (PetscInt q = qStart; q < qEnd; ++q) {
4560         const PetscInt *support;
4561         PetscInt        supportSize;
4562 
4563         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4564         PetscCall(DMPlexGetSupport(dm, q, &support));
4565         for (PetscInt s = 0; s < supportSize; ++s) {
4566           sMin = PetscMin(support[s], sMin);
4567           sMax = PetscMax(support[s], sMax);
4568         }
4569       }
4570       PetscCall(DMLabelGetNumValues(label, &level));
4571       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4572       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4573     }
4574   }
4575   PetscFunctionReturn(PETSC_SUCCESS);
4576 }
4577 
4578 /*@
4579   DMPlexStratify - Computes the strata for all points in the `DMPLEX`
4580 
4581   Collective
4582 
4583   Input Parameter:
4584 . dm - The `DMPLEX`
4585 
4586   Level: beginner
4587 
4588   Notes:
4589   The strata group all points of the same grade, and this function calculates the strata. This
4590   grade can be seen as the height (or depth) of the point in the DAG.
4591 
4592   The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4593   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram).
4594   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4595   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4596   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4597   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4598   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4599 
4600   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4601   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4602   we had a mesh consisting of one triangle (c0) and three vertices (v0, v1, v2), and only one edge is on the boundary so we choose
4603   to interpolate only that one (e0), so that
4604 .vb
4605   cone(c0) = {e0, v2}
4606   cone(e0) = {v0, v1}
4607 .ve
4608   If `DMPlexStratify()` is run on this mesh, it will give depths
4609 .vb
4610    depth 0 = {v0, v1, v2}
4611    depth 1 = {e0, c0}
4612 .ve
4613   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4614 
4615   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4616 
4617 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4618 @*/
4619 PetscErrorCode DMPlexStratify(DM dm)
4620 {
4621   DM_Plex  *mesh = (DM_Plex *)dm->data;
4622   DMLabel   label;
4623   PetscBool flg = PETSC_FALSE;
4624 
4625   PetscFunctionBegin;
4626   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4627   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4628 
4629   // Create depth label
4630   PetscCall(DMRemoveLabel(dm, "depth", NULL));
4631   PetscCall(DMCreateLabel(dm, "depth"));
4632   PetscCall(DMPlexGetDepthLabel(dm, &label));
4633 
4634   PetscCall(PetscOptionsGetBool(NULL, dm->hdr.prefix, "-dm_plex_stratify_celltype", &flg, NULL));
4635   if (flg) PetscCall(DMPlexStratify_CellType_Private(dm, label));
4636   else PetscCall(DMPlexStratify_Topological_Private(dm, label));
4637 
4638   { /* just in case there is an empty process */
4639     PetscInt numValues, maxValues = 0, v;
4640 
4641     PetscCall(DMLabelGetNumValues(label, &numValues));
4642     PetscCallMPI(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4643     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4644   }
4645   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4646   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4647   PetscFunctionReturn(PETSC_SUCCESS);
4648 }
4649 
4650 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4651 {
4652   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4653   PetscInt       dim, depth, pheight, coneSize;
4654   PetscBool      preferTensor;
4655 
4656   PetscFunctionBeginHot;
4657   PetscCall(DMGetDimension(dm, &dim));
4658   PetscCall(DMPlexGetDepth(dm, &depth));
4659   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4660   PetscCall(DMPlexGetInterpolatePreferTensor(dm, &preferTensor));
4661   pheight = depth - pdepth;
4662   if (depth <= 1) {
4663     switch (pdepth) {
4664     case 0:
4665       ct = DM_POLYTOPE_POINT;
4666       break;
4667     case 1:
4668       switch (coneSize) {
4669       case 2:
4670         ct = DM_POLYTOPE_SEGMENT;
4671         break;
4672       case 3:
4673         ct = DM_POLYTOPE_TRIANGLE;
4674         break;
4675       case 4:
4676         switch (dim) {
4677         case 2:
4678           ct = DM_POLYTOPE_QUADRILATERAL;
4679           break;
4680         case 3:
4681           ct = DM_POLYTOPE_TETRAHEDRON;
4682           break;
4683         default:
4684           break;
4685         }
4686         break;
4687       case 5:
4688         ct = DM_POLYTOPE_PYRAMID;
4689         break;
4690       case 6:
4691         ct = preferTensor ? DM_POLYTOPE_TRI_PRISM_TENSOR : DM_POLYTOPE_TRI_PRISM;
4692         break;
4693       case 8:
4694         ct = DM_POLYTOPE_HEXAHEDRON;
4695         break;
4696       default:
4697         break;
4698       }
4699     }
4700   } else {
4701     if (pdepth == 0) {
4702       ct = DM_POLYTOPE_POINT;
4703     } else if (pheight == 0) {
4704       switch (dim) {
4705       case 1:
4706         switch (coneSize) {
4707         case 2:
4708           ct = DM_POLYTOPE_SEGMENT;
4709           break;
4710         default:
4711           break;
4712         }
4713         break;
4714       case 2:
4715         switch (coneSize) {
4716         case 3:
4717           ct = DM_POLYTOPE_TRIANGLE;
4718           break;
4719         case 4:
4720           ct = DM_POLYTOPE_QUADRILATERAL;
4721           break;
4722         default:
4723           break;
4724         }
4725         break;
4726       case 3:
4727         switch (coneSize) {
4728         case 4:
4729           ct = DM_POLYTOPE_TETRAHEDRON;
4730           break;
4731         case 5: {
4732           const PetscInt *cone;
4733           PetscInt        faceConeSize;
4734 
4735           PetscCall(DMPlexGetCone(dm, p, &cone));
4736           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4737           switch (faceConeSize) {
4738           case 3:
4739             ct = preferTensor ? DM_POLYTOPE_TRI_PRISM_TENSOR : DM_POLYTOPE_TRI_PRISM;
4740             break;
4741           case 4:
4742             ct = DM_POLYTOPE_PYRAMID;
4743             break;
4744           }
4745         } break;
4746         case 6:
4747           ct = DM_POLYTOPE_HEXAHEDRON;
4748           break;
4749         default:
4750           break;
4751         }
4752         break;
4753       default:
4754         break;
4755       }
4756     } else if (pheight > 0) {
4757       switch (coneSize) {
4758       case 2:
4759         ct = DM_POLYTOPE_SEGMENT;
4760         break;
4761       case 3:
4762         ct = DM_POLYTOPE_TRIANGLE;
4763         break;
4764       case 4:
4765         ct = DM_POLYTOPE_QUADRILATERAL;
4766         break;
4767       default:
4768         break;
4769       }
4770     }
4771   }
4772   *pt = ct;
4773   PetscFunctionReturn(PETSC_SUCCESS);
4774 }
4775 
4776 /*@
4777   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4778 
4779   Collective
4780 
4781   Input Parameter:
4782 . dm - The `DMPLEX`
4783 
4784   Level: developer
4785 
4786   Note:
4787   This function is normally called automatically when a cell type is requested. It creates an
4788   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4789   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4790 
4791   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4792 
4793 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4794 @*/
4795 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4796 {
4797   DM_Plex *mesh;
4798   DMLabel  ctLabel;
4799   PetscInt pStart, pEnd, p;
4800 
4801   PetscFunctionBegin;
4802   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4803   mesh = (DM_Plex *)dm->data;
4804   PetscCall(DMCreateLabel(dm, "celltype"));
4805   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4806   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4807   PetscCall(PetscFree(mesh->cellTypes));
4808   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4809   for (p = pStart; p < pEnd; ++p) {
4810     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4811     PetscInt       pdepth;
4812 
4813     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4814     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4815     PetscCheck(ct != DM_POLYTOPE_UNKNOWN && ct != DM_POLYTOPE_UNKNOWN_CELL && ct != DM_POLYTOPE_UNKNOWN_FACE, PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " has invalid celltype (%s)", p, DMPolytopeTypes[ct]);
4816     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4817     mesh->cellTypes[p - pStart].value_as_uint8 = (uint8_t)ct;
4818   }
4819   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4820   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4821   PetscFunctionReturn(PETSC_SUCCESS);
4822 }
4823 
4824 /*@C
4825   DMPlexGetJoin - Get an array for the join of the set of points
4826 
4827   Not Collective
4828 
4829   Input Parameters:
4830 + dm        - The `DMPLEX` object
4831 . numPoints - The number of input points for the join
4832 - points    - The input points
4833 
4834   Output Parameters:
4835 + numCoveredPoints - The number of points in the join
4836 - coveredPoints    - The points in the join
4837 
4838   Level: intermediate
4839 
4840   Note:
4841   Currently, this is restricted to a single level join
4842 
4843   Fortran Notes:
4844   `converedPoints` must be declared with
4845 .vb
4846   PetscInt, pointer :: coveredPints(:)
4847 .ve
4848 
4849 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4850 @*/
4851 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4852 {
4853   DM_Plex  *mesh = (DM_Plex *)dm->data;
4854   PetscInt *join[2];
4855   PetscInt  joinSize, i = 0;
4856   PetscInt  dof, off, p, c, m;
4857   PetscInt  maxSupportSize;
4858 
4859   PetscFunctionBegin;
4860   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4861   PetscAssertPointer(points, 3);
4862   PetscAssertPointer(numCoveredPoints, 4);
4863   PetscAssertPointer(coveredPoints, 5);
4864   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4865   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4866   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4867   /* Copy in support of first point */
4868   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4869   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4870   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4871   /* Check each successive support */
4872   for (p = 1; p < numPoints; ++p) {
4873     PetscInt newJoinSize = 0;
4874 
4875     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4876     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4877     for (c = 0; c < dof; ++c) {
4878       const PetscInt point = mesh->supports[off + c];
4879 
4880       for (m = 0; m < joinSize; ++m) {
4881         if (point == join[i][m]) {
4882           join[1 - i][newJoinSize++] = point;
4883           break;
4884         }
4885       }
4886     }
4887     joinSize = newJoinSize;
4888     i        = 1 - i;
4889   }
4890   *numCoveredPoints = joinSize;
4891   *coveredPoints    = join[i];
4892   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4893   PetscFunctionReturn(PETSC_SUCCESS);
4894 }
4895 
4896 /*@C
4897   DMPlexRestoreJoin - Restore an array for the join of the set of points obtained with `DMPlexGetJoin()`
4898 
4899   Not Collective
4900 
4901   Input Parameters:
4902 + dm        - The `DMPLEX` object
4903 . numPoints - The number of input points for the join
4904 - points    - The input points
4905 
4906   Output Parameters:
4907 + numCoveredPoints - The number of points in the join
4908 - coveredPoints    - The points in the join
4909 
4910   Level: intermediate
4911 
4912   Fortran Notes:
4913   `converedPoints` must be declared with
4914 .vb
4915   PetscInt, pointer :: coveredPoints(:)
4916 .ve
4917 
4918   Pass `PETSC_NULL_INTEGER` for `numCoveredPoints` if it is not needed
4919 
4920 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4921 @*/
4922 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4923 {
4924   PetscFunctionBegin;
4925   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4926   if (points) PetscAssertPointer(points, 3);
4927   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4928   PetscAssertPointer(coveredPoints, 5);
4929   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4930   if (numCoveredPoints) *numCoveredPoints = 0;
4931   PetscFunctionReturn(PETSC_SUCCESS);
4932 }
4933 
4934 /*@C
4935   DMPlexGetFullJoin - Get an array for the join of the set of points
4936 
4937   Not Collective
4938 
4939   Input Parameters:
4940 + dm        - The `DMPLEX` object
4941 . numPoints - The number of input points for the join
4942 - points    - The input points, its length is `numPoints`
4943 
4944   Output Parameters:
4945 + numCoveredPoints - The number of points in the join
4946 - coveredPoints    - The points in the join, its length is `numCoveredPoints`
4947 
4948   Level: intermediate
4949 
4950   Fortran Notes:
4951 .vb
4952   PetscInt, pointer :: coveredPints(:)
4953 .ve
4954 
4955 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4956 @*/
4957 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4958 {
4959   PetscInt *offsets, **closures;
4960   PetscInt *join[2];
4961   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4962   PetscInt  p, d, c, m, ms;
4963 
4964   PetscFunctionBegin;
4965   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4966   PetscAssertPointer(points, 3);
4967   PetscAssertPointer(numCoveredPoints, 4);
4968   PetscAssertPointer(coveredPoints, 5);
4969 
4970   PetscCall(DMPlexGetDepth(dm, &depth));
4971   PetscCall(PetscCalloc1(numPoints, &closures));
4972   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4973   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4974   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4975   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4976   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4977 
4978   for (p = 0; p < numPoints; ++p) {
4979     PetscInt closureSize;
4980 
4981     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4982 
4983     offsets[p * (depth + 2) + 0] = 0;
4984     for (d = 0; d < depth + 1; ++d) {
4985       PetscInt pStart, pEnd, i;
4986 
4987       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4988       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4989         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4990           offsets[p * (depth + 2) + d + 1] = i;
4991           break;
4992         }
4993       }
4994       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4995     }
4996     PetscCheck(offsets[p * (depth + 2) + depth + 1] == closureSize, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Total size of closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[p * (depth + 2) + depth + 1], closureSize);
4997   }
4998   for (d = 0; d < depth + 1; ++d) {
4999     PetscInt dof;
5000 
5001     /* Copy in support of first point */
5002     dof = offsets[d + 1] - offsets[d];
5003     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
5004     /* Check each successive cone */
5005     for (p = 1; p < numPoints && joinSize; ++p) {
5006       PetscInt newJoinSize = 0;
5007 
5008       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
5009       for (c = 0; c < dof; ++c) {
5010         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
5011 
5012         for (m = 0; m < joinSize; ++m) {
5013           if (point == join[i][m]) {
5014             join[1 - i][newJoinSize++] = point;
5015             break;
5016           }
5017         }
5018       }
5019       joinSize = newJoinSize;
5020       i        = 1 - i;
5021     }
5022     if (joinSize) break;
5023   }
5024   *numCoveredPoints = joinSize;
5025   *coveredPoints    = join[i];
5026   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
5027   PetscCall(PetscFree(closures));
5028   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
5029   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
5030   PetscFunctionReturn(PETSC_SUCCESS);
5031 }
5032 
5033 /*@C
5034   DMPlexGetMeet - Get an array for the meet of the set of points
5035 
5036   Not Collective
5037 
5038   Input Parameters:
5039 + dm        - The `DMPLEX` object
5040 . numPoints - The number of input points for the meet
5041 - points    - The input points, of length `numPoints`
5042 
5043   Output Parameters:
5044 + numCoveringPoints - The number of points in the meet
5045 - coveringPoints    - The points in the meet, of length `numCoveringPoints`
5046 
5047   Level: intermediate
5048 
5049   Note:
5050   Currently, this is restricted to a single level meet
5051 
5052   Fortran Note:
5053   `coveringPoints` must be declared with
5054 .vb
5055   PetscInt, pointer :: coveringPoints(:)
5056 .ve
5057 
5058 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
5059 @*/
5060 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt *coveringPoints[])
5061 {
5062   DM_Plex  *mesh = (DM_Plex *)dm->data;
5063   PetscInt *meet[2];
5064   PetscInt  meetSize, i = 0;
5065   PetscInt  dof, off, p, c, m;
5066   PetscInt  maxConeSize;
5067 
5068   PetscFunctionBegin;
5069   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5070   PetscAssertPointer(points, 3);
5071   PetscAssertPointer(numCoveringPoints, 4);
5072   PetscAssertPointer(coveringPoints, 5);
5073   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
5074   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
5075   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
5076   /* Copy in cone of first point */
5077   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
5078   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
5079   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
5080   /* Check each successive cone */
5081   for (p = 1; p < numPoints; ++p) {
5082     PetscInt newMeetSize = 0;
5083 
5084     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
5085     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
5086     for (c = 0; c < dof; ++c) {
5087       const PetscInt point = mesh->cones[off + c];
5088 
5089       for (m = 0; m < meetSize; ++m) {
5090         if (point == meet[i][m]) {
5091           meet[1 - i][newMeetSize++] = point;
5092           break;
5093         }
5094       }
5095     }
5096     meetSize = newMeetSize;
5097     i        = 1 - i;
5098   }
5099   *numCoveringPoints = meetSize;
5100   *coveringPoints    = meet[i];
5101   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
5102   PetscFunctionReturn(PETSC_SUCCESS);
5103 }
5104 
5105 /*@C
5106   DMPlexRestoreMeet - Restore an array for the meet of the set of points obtained with `DMPlexGetMeet()`
5107 
5108   Not Collective
5109 
5110   Input Parameters:
5111 + dm        - The `DMPLEX` object
5112 . numPoints - The number of input points for the meet
5113 - points    - The input points
5114 
5115   Output Parameters:
5116 + numCoveredPoints - The number of points in the meet
5117 - coveredPoints    - The points in the meet
5118 
5119   Level: intermediate
5120 
5121 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
5122 @*/
5123 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
5124 {
5125   PetscFunctionBegin;
5126   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5127   if (points) PetscAssertPointer(points, 3);
5128   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
5129   PetscAssertPointer(coveredPoints, 5);
5130   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
5131   if (numCoveredPoints) *numCoveredPoints = 0;
5132   PetscFunctionReturn(PETSC_SUCCESS);
5133 }
5134 
5135 /*@C
5136   DMPlexGetFullMeet - Get an array for the meet of the set of points
5137 
5138   Not Collective
5139 
5140   Input Parameters:
5141 + dm        - The `DMPLEX` object
5142 . numPoints - The number of input points for the meet
5143 - points    - The input points, of length  `numPoints`
5144 
5145   Output Parameters:
5146 + numCoveredPoints - The number of points in the meet
5147 - coveredPoints    - The points in the meet, of length  `numCoveredPoints`
5148 
5149   Level: intermediate
5150 
5151   Fortran Notes:
5152   `coveredPoints` must be declared with
5153 .vb
5154   PetscInt, pointer :: coveredPoints(:)
5155 .ve
5156 
5157 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
5158 @*/
5159 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
5160 {
5161   PetscInt *offsets, **closures;
5162   PetscInt *meet[2];
5163   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
5164   PetscInt  p, h, c, m, mc;
5165 
5166   PetscFunctionBegin;
5167   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5168   PetscAssertPointer(points, 3);
5169   PetscAssertPointer(numCoveredPoints, 4);
5170   PetscAssertPointer(coveredPoints, 5);
5171 
5172   PetscCall(DMPlexGetDepth(dm, &height));
5173   PetscCall(PetscMalloc1(numPoints, &closures));
5174   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5175   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
5176   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
5177   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
5178   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
5179 
5180   for (p = 0; p < numPoints; ++p) {
5181     PetscInt closureSize;
5182 
5183     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
5184 
5185     offsets[p * (height + 2) + 0] = 0;
5186     for (h = 0; h < height + 1; ++h) {
5187       PetscInt pStart, pEnd, i;
5188 
5189       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
5190       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
5191         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
5192           offsets[p * (height + 2) + h + 1] = i;
5193           break;
5194         }
5195       }
5196       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
5197     }
5198     PetscCheck(offsets[p * (height + 2) + height + 1] == closureSize, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Total size of closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[p * (height + 2) + height + 1], closureSize);
5199   }
5200   for (h = 0; h < height + 1; ++h) {
5201     PetscInt dof;
5202 
5203     /* Copy in cone of first point */
5204     dof = offsets[h + 1] - offsets[h];
5205     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
5206     /* Check each successive cone */
5207     for (p = 1; p < numPoints && meetSize; ++p) {
5208       PetscInt newMeetSize = 0;
5209 
5210       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
5211       for (c = 0; c < dof; ++c) {
5212         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
5213 
5214         for (m = 0; m < meetSize; ++m) {
5215           if (point == meet[i][m]) {
5216             meet[1 - i][newMeetSize++] = point;
5217             break;
5218           }
5219         }
5220       }
5221       meetSize = newMeetSize;
5222       i        = 1 - i;
5223     }
5224     if (meetSize) break;
5225   }
5226   *numCoveredPoints = meetSize;
5227   *coveredPoints    = meet[i];
5228   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
5229   PetscCall(PetscFree(closures));
5230   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5231   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
5232   PetscFunctionReturn(PETSC_SUCCESS);
5233 }
5234 
5235 /*@
5236   DMPlexEqual - Determine if two `DM` have the same topology
5237 
5238   Not Collective
5239 
5240   Input Parameters:
5241 + dmA - A `DMPLEX` object
5242 - dmB - A `DMPLEX` object
5243 
5244   Output Parameter:
5245 . equal - `PETSC_TRUE` if the topologies are identical
5246 
5247   Level: intermediate
5248 
5249   Note:
5250   We are not solving graph isomorphism, so we do not permute.
5251 
5252 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5253 @*/
5254 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
5255 {
5256   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
5257 
5258   PetscFunctionBegin;
5259   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
5260   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
5261   PetscAssertPointer(equal, 3);
5262 
5263   *equal = PETSC_FALSE;
5264   PetscCall(DMPlexGetDepth(dmA, &depth));
5265   PetscCall(DMPlexGetDepth(dmB, &depthB));
5266   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
5267   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
5268   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
5269   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
5270   for (p = pStart; p < pEnd; ++p) {
5271     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
5272     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
5273 
5274     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
5275     PetscCall(DMPlexGetCone(dmA, p, &cone));
5276     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
5277     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
5278     PetscCall(DMPlexGetCone(dmB, p, &coneB));
5279     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
5280     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5281     for (c = 0; c < coneSize; ++c) {
5282       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5283       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5284     }
5285     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
5286     PetscCall(DMPlexGetSupport(dmA, p, &support));
5287     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
5288     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
5289     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5290     for (s = 0; s < supportSize; ++s) {
5291       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
5292     }
5293   }
5294   *equal = PETSC_TRUE;
5295   PetscFunctionReturn(PETSC_SUCCESS);
5296 }
5297 
5298 /*@
5299   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
5300 
5301   Not Collective
5302 
5303   Input Parameters:
5304 + dm         - The `DMPLEX`
5305 . cellDim    - The cell dimension
5306 - numCorners - The number of vertices on a cell
5307 
5308   Output Parameter:
5309 . numFaceVertices - The number of vertices on a face
5310 
5311   Level: developer
5312 
5313   Note:
5314   Of course this can only work for a restricted set of symmetric shapes
5315 
5316 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5317 @*/
5318 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
5319 {
5320   MPI_Comm comm;
5321 
5322   PetscFunctionBegin;
5323   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
5324   PetscAssertPointer(numFaceVertices, 4);
5325   switch (cellDim) {
5326   case 0:
5327     *numFaceVertices = 0;
5328     break;
5329   case 1:
5330     *numFaceVertices = 1;
5331     break;
5332   case 2:
5333     switch (numCorners) {
5334     case 3:                 /* triangle */
5335       *numFaceVertices = 2; /* Edge has 2 vertices */
5336       break;
5337     case 4:                 /* quadrilateral */
5338       *numFaceVertices = 2; /* Edge has 2 vertices */
5339       break;
5340     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
5341       *numFaceVertices = 3; /* Edge has 3 vertices */
5342       break;
5343     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
5344       *numFaceVertices = 3; /* Edge has 3 vertices */
5345       break;
5346     default:
5347       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5348     }
5349     break;
5350   case 3:
5351     switch (numCorners) {
5352     case 4:                 /* tetradehdron */
5353       *numFaceVertices = 3; /* Face has 3 vertices */
5354       break;
5355     case 6:                 /* tet cohesive cells */
5356       *numFaceVertices = 4; /* Face has 4 vertices */
5357       break;
5358     case 8:                 /* hexahedron */
5359       *numFaceVertices = 4; /* Face has 4 vertices */
5360       break;
5361     case 9:                 /* tet cohesive Lagrange cells */
5362       *numFaceVertices = 6; /* Face has 6 vertices */
5363       break;
5364     case 10:                /* quadratic tetrahedron */
5365       *numFaceVertices = 6; /* Face has 6 vertices */
5366       break;
5367     case 12:                /* hex cohesive Lagrange cells */
5368       *numFaceVertices = 6; /* Face has 6 vertices */
5369       break;
5370     case 18:                /* quadratic tet cohesive Lagrange cells */
5371       *numFaceVertices = 6; /* Face has 6 vertices */
5372       break;
5373     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
5374       *numFaceVertices = 9; /* Face has 9 vertices */
5375       break;
5376     default:
5377       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5378     }
5379     break;
5380   default:
5381     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
5382   }
5383   PetscFunctionReturn(PETSC_SUCCESS);
5384 }
5385 
5386 /*@
5387   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
5388 
5389   Not Collective
5390 
5391   Input Parameter:
5392 . dm - The `DMPLEX` object
5393 
5394   Output Parameter:
5395 . depthLabel - The `DMLabel` recording point depth
5396 
5397   Level: developer
5398 
5399 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
5400 @*/
5401 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
5402 {
5403   PetscFunctionBegin;
5404   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5405   PetscAssertPointer(depthLabel, 2);
5406   *depthLabel = dm->depthLabel;
5407   PetscFunctionReturn(PETSC_SUCCESS);
5408 }
5409 
5410 /*@
5411   DMPlexGetDepth - Get the depth of the DAG representing this mesh
5412 
5413   Not Collective
5414 
5415   Input Parameter:
5416 . dm - The `DMPLEX` object
5417 
5418   Output Parameter:
5419 . depth - The number of strata (breadth first levels) in the DAG
5420 
5421   Level: developer
5422 
5423   Notes:
5424   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
5425 
5426   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
5427 
5428   An empty mesh gives -1.
5429 
5430 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
5431 @*/
5432 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
5433 {
5434   DM_Plex *mesh = (DM_Plex *)dm->data;
5435   DMLabel  label;
5436   PetscInt d = -1;
5437 
5438   PetscFunctionBegin;
5439   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5440   PetscAssertPointer(depth, 2);
5441   if (mesh->tr) {
5442     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
5443   } else {
5444     PetscCall(DMPlexGetDepthLabel(dm, &label));
5445     // Allow missing depths
5446     if (label) PetscCall(DMLabelGetValueBounds(label, NULL, &d));
5447     *depth = d;
5448   }
5449   PetscFunctionReturn(PETSC_SUCCESS);
5450 }
5451 
5452 /*@
5453   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
5454 
5455   Not Collective
5456 
5457   Input Parameters:
5458 + dm    - The `DMPLEX` object
5459 - depth - The requested depth
5460 
5461   Output Parameters:
5462 + start - The first point at this `depth`
5463 - end   - One beyond the last point at this `depth`
5464 
5465   Level: developer
5466 
5467   Notes:
5468   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5469   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5470   higher dimension, e.g., "edges".
5471 
5472 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5473 @*/
5474 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PeOp PetscInt *start, PeOp PetscInt *end)
5475 {
5476   DM_Plex *mesh = (DM_Plex *)dm->data;
5477   DMLabel  label;
5478   PetscInt pStart, pEnd;
5479 
5480   PetscFunctionBegin;
5481   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5482   if (start) {
5483     PetscAssertPointer(start, 3);
5484     *start = 0;
5485   }
5486   if (end) {
5487     PetscAssertPointer(end, 4);
5488     *end = 0;
5489   }
5490   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5491   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5492   if (depth < 0) {
5493     if (start) *start = pStart;
5494     if (end) *end = pEnd;
5495     PetscFunctionReturn(PETSC_SUCCESS);
5496   }
5497   if (mesh->tr) {
5498     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5499   } else {
5500     PetscCall(DMPlexGetDepthLabel(dm, &label));
5501     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5502     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5503   }
5504   PetscFunctionReturn(PETSC_SUCCESS);
5505 }
5506 
5507 /*@
5508   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5509 
5510   Not Collective
5511 
5512   Input Parameters:
5513 + dm     - The `DMPLEX` object
5514 - height - The requested height
5515 
5516   Output Parameters:
5517 + start - The first point at this `height`
5518 - end   - One beyond the last point at this `height`
5519 
5520   Level: developer
5521 
5522   Notes:
5523   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5524   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5525   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5526 
5527 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5528 @*/
5529 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PeOp PetscInt *start, PeOp PetscInt *end)
5530 {
5531   DMLabel  label;
5532   PetscInt depth, pStart, pEnd;
5533 
5534   PetscFunctionBegin;
5535   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5536   if (start) {
5537     PetscAssertPointer(start, 3);
5538     *start = 0;
5539   }
5540   if (end) {
5541     PetscAssertPointer(end, 4);
5542     *end = 0;
5543   }
5544   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5545   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5546   if (height < 0) {
5547     if (start) *start = pStart;
5548     if (end) *end = pEnd;
5549     PetscFunctionReturn(PETSC_SUCCESS);
5550   }
5551   PetscCall(DMPlexGetDepthLabel(dm, &label));
5552   if (label) PetscCall(DMLabelGetNumValues(label, &depth));
5553   else PetscCall(DMGetDimension(dm, &depth));
5554   PetscCheck(depth >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Depth not yet computed");
5555   PetscCall(DMPlexGetDepthStratum(dm, depth - 1 - height, start, end));
5556   PetscFunctionReturn(PETSC_SUCCESS);
5557 }
5558 
5559 /*@
5560   DMPlexGetPointDepth - Get the `depth` of a given point
5561 
5562   Not Collective
5563 
5564   Input Parameters:
5565 + dm    - The `DMPLEX` object
5566 - point - The point
5567 
5568   Output Parameter:
5569 . depth - The depth of the `point`
5570 
5571   Level: intermediate
5572 
5573 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5574 @*/
5575 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5576 {
5577   PetscFunctionBegin;
5578   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5579   PetscAssertPointer(depth, 3);
5580   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5581   PetscFunctionReturn(PETSC_SUCCESS);
5582 }
5583 
5584 /*@
5585   DMPlexGetPointHeight - Get the `height` of a given point
5586 
5587   Not Collective
5588 
5589   Input Parameters:
5590 + dm    - The `DMPLEX` object
5591 - point - The point
5592 
5593   Output Parameter:
5594 . height - The height of the `point`
5595 
5596   Level: intermediate
5597 
5598 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5599 @*/
5600 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5601 {
5602   PetscInt n, pDepth;
5603 
5604   PetscFunctionBegin;
5605   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5606   PetscAssertPointer(height, 3);
5607   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5608   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5609   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5610   PetscFunctionReturn(PETSC_SUCCESS);
5611 }
5612 
5613 /*@
5614   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5615 
5616   Not Collective
5617 
5618   Input Parameter:
5619 . dm - The `DMPLEX` object
5620 
5621   Output Parameter:
5622 . celltypeLabel - The `DMLabel` recording cell polytope type
5623 
5624   Level: developer
5625 
5626   Note:
5627   This function will trigger automatica computation of cell types. This can be disabled by calling
5628   `DMCreateLabel`(dm, "celltype") beforehand.
5629 
5630 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5631 @*/
5632 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5633 {
5634   PetscFunctionBegin;
5635   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5636   PetscAssertPointer(celltypeLabel, 2);
5637   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5638   *celltypeLabel = dm->celltypeLabel;
5639   PetscFunctionReturn(PETSC_SUCCESS);
5640 }
5641 
5642 /*@
5643   DMPlexGetCellType - Get the polytope type of a given cell
5644 
5645   Not Collective
5646 
5647   Input Parameters:
5648 + dm   - The `DMPLEX` object
5649 - cell - The cell
5650 
5651   Output Parameter:
5652 . celltype - The polytope type of the cell
5653 
5654   Level: intermediate
5655 
5656 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5657 @*/
5658 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5659 {
5660   DM_Plex *mesh = (DM_Plex *)dm->data;
5661   DMLabel  label;
5662   PetscInt ct;
5663 
5664   PetscFunctionBegin;
5665   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5666   PetscAssertPointer(celltype, 3);
5667   if (mesh->tr) {
5668     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5669   } else {
5670     PetscInt pStart, pEnd;
5671 
5672     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5673     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5674       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5675       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5676       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5677       for (PetscInt p = pStart; p < pEnd; p++) {
5678         PetscCall(DMLabelGetValue(label, p, &ct));
5679         mesh->cellTypes[p - pStart].value_as_uint8 = (uint8_t)ct;
5680       }
5681     }
5682     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5683     if (PetscDefined(USE_DEBUG)) {
5684       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5685       PetscCall(DMLabelGetValue(label, cell, &ct));
5686       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5687       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5688     }
5689   }
5690   PetscFunctionReturn(PETSC_SUCCESS);
5691 }
5692 
5693 /*@
5694   DMPlexSetCellType - Set the polytope type of a given cell
5695 
5696   Not Collective
5697 
5698   Input Parameters:
5699 + dm       - The `DMPLEX` object
5700 . cell     - The cell
5701 - celltype - The polytope type of the cell
5702 
5703   Level: advanced
5704 
5705   Note:
5706   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5707   is executed. This function will override the computed type. However, if automatic classification will not succeed
5708   and a user wants to manually specify all types, the classification must be disabled by calling
5709   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5710 
5711 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5712 @*/
5713 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5714 {
5715   DM_Plex *mesh = (DM_Plex *)dm->data;
5716   DMLabel  label;
5717   PetscInt pStart, pEnd;
5718 
5719   PetscFunctionBegin;
5720   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5721   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5722   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5723   PetscCall(DMLabelSetValue(label, cell, celltype));
5724   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5725   mesh->cellTypes[cell - pStart].value_as_uint8 = (uint8_t)celltype;
5726   PetscFunctionReturn(PETSC_SUCCESS);
5727 }
5728 
5729 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5730 {
5731   PetscSection section;
5732   PetscInt     maxHeight;
5733   const char  *prefix;
5734 
5735   PetscFunctionBegin;
5736   PetscCall(DMClone(dm, cdm));
5737   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5738   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5739   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5740   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5741   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5742   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5743   PetscCall(DMSetLocalSection(*cdm, section));
5744   PetscCall(PetscSectionDestroy(&section));
5745 
5746   PetscCall(DMSetNumFields(*cdm, 1));
5747   PetscCall(DMCreateDS(*cdm));
5748   (*cdm)->cloneOpts = PETSC_TRUE;
5749   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5750   PetscFunctionReturn(PETSC_SUCCESS);
5751 }
5752 
5753 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5754 {
5755   Vec coordsLocal, cellCoordsLocal;
5756   DM  coordsDM, cellCoordsDM;
5757 
5758   PetscFunctionBegin;
5759   *field = NULL;
5760   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5761   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5762   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5763   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5764   if (coordsLocal && coordsDM) {
5765     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5766     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5767   }
5768   PetscFunctionReturn(PETSC_SUCCESS);
5769 }
5770 
5771 /*@
5772   DMPlexGetConeSection - Return a section which describes the layout of cone data
5773 
5774   Not Collective
5775 
5776   Input Parameter:
5777 . dm - The `DMPLEX` object
5778 
5779   Output Parameter:
5780 . section - The `PetscSection` object
5781 
5782   Level: developer
5783 
5784 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5785 @*/
5786 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5787 {
5788   DM_Plex *mesh = (DM_Plex *)dm->data;
5789 
5790   PetscFunctionBegin;
5791   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5792   if (section) *section = mesh->coneSection;
5793   PetscFunctionReturn(PETSC_SUCCESS);
5794 }
5795 
5796 /*@
5797   DMPlexGetSupportSection - Return a section which describes the layout of support data
5798 
5799   Not Collective
5800 
5801   Input Parameter:
5802 . dm - The `DMPLEX` object
5803 
5804   Output Parameter:
5805 . section - The `PetscSection` object
5806 
5807   Level: developer
5808 
5809 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5810 @*/
5811 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5812 {
5813   DM_Plex *mesh = (DM_Plex *)dm->data;
5814 
5815   PetscFunctionBegin;
5816   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5817   if (section) *section = mesh->supportSection;
5818   PetscFunctionReturn(PETSC_SUCCESS);
5819 }
5820 
5821 /*@C
5822   DMPlexGetCones - Return cone data
5823 
5824   Not Collective
5825 
5826   Input Parameter:
5827 . dm - The `DMPLEX` object
5828 
5829   Output Parameter:
5830 . cones - The cone for each point
5831 
5832   Level: developer
5833 
5834 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5835 @*/
5836 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5837 {
5838   DM_Plex *mesh = (DM_Plex *)dm->data;
5839 
5840   PetscFunctionBegin;
5841   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5842   if (cones) *cones = mesh->cones;
5843   PetscFunctionReturn(PETSC_SUCCESS);
5844 }
5845 
5846 /*@C
5847   DMPlexGetConeOrientations - Return cone orientation data
5848 
5849   Not Collective
5850 
5851   Input Parameter:
5852 . dm - The `DMPLEX` object
5853 
5854   Output Parameter:
5855 . coneOrientations - The array of cone orientations for all points
5856 
5857   Level: developer
5858 
5859   Notes:
5860   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points
5861   as returned by `DMPlexGetConeOrientation()`.
5862 
5863   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5864 
5865 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5866 @*/
5867 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5868 {
5869   DM_Plex *mesh = (DM_Plex *)dm->data;
5870 
5871   PetscFunctionBegin;
5872   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5873   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5874   PetscFunctionReturn(PETSC_SUCCESS);
5875 }
5876 
5877 /* FEM Support */
5878 
5879 PetscErrorCode DMPlexGetAllCells_Internal(DM plex, IS *cellIS)
5880 {
5881   PetscInt depth;
5882 
5883   PetscFunctionBegin;
5884   PetscCall(DMPlexGetDepth(plex, &depth));
5885   PetscCall(DMGetStratumIS(plex, "dim", depth, cellIS));
5886   if (!*cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, cellIS));
5887   PetscFunctionReturn(PETSC_SUCCESS);
5888 }
5889 
5890 PetscErrorCode DMPlexGetAllFaces_Internal(DM plex, IS *faceIS)
5891 {
5892   PetscInt depth;
5893 
5894   PetscFunctionBegin;
5895   PetscCall(DMPlexGetDepth(plex, &depth));
5896   PetscCall(DMGetStratumIS(plex, "dim", depth - 1, faceIS));
5897   if (!*faceIS) PetscCall(DMGetStratumIS(plex, "depth", depth - 1, faceIS));
5898   PetscFunctionReturn(PETSC_SUCCESS);
5899 }
5900 
5901 /*
5902  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5903  representing a line in the section.
5904 */
5905 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(DM dm, PetscSection section, PetscInt field, PetscInt line, PetscInt *Nc, PetscInt *k, PetscBool *continuous, PetscBool *tensor)
5906 {
5907   PetscObject  obj;
5908   PetscClassId id;
5909   PetscFE      fe = NULL;
5910 
5911   PetscFunctionBeginHot;
5912   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5913   PetscCall(DMGetField(dm, field, NULL, &obj));
5914   PetscCall(PetscObjectGetClassId(obj, &id));
5915   if (id == PETSCFE_CLASSID) fe = (PetscFE)obj;
5916 
5917   if (!fe) {
5918     /* Assume the full interpolated mesh is in the chart; lines in particular */
5919     /* An order k SEM disc has k-1 dofs on an edge */
5920     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5921     *k = *k / *Nc + 1;
5922   } else {
5923     PetscInt       dual_space_size, dim;
5924     PetscDualSpace dsp;
5925 
5926     PetscCall(DMGetDimension(dm, &dim));
5927     PetscCall(PetscFEGetDualSpace(fe, &dsp));
5928     PetscCall(PetscDualSpaceGetDimension(dsp, &dual_space_size));
5929     *k = (PetscInt)PetscCeilReal(PetscPowReal(dual_space_size / *Nc, 1.0 / dim)) - 1;
5930     PetscCall(PetscDualSpaceLagrangeGetContinuity(dsp, continuous));
5931     PetscCall(PetscDualSpaceLagrangeGetTensor(dsp, tensor));
5932   }
5933   PetscFunctionReturn(PETSC_SUCCESS);
5934 }
5935 
5936 static PetscErrorCode GetFieldSize_Private(PetscInt dim, PetscInt k, PetscBool tensor, PetscInt *dof)
5937 {
5938   PetscFunctionBeginHot;
5939   if (tensor) {
5940     *dof = PetscPowInt(k + 1, dim);
5941   } else {
5942     switch (dim) {
5943     case 1:
5944       *dof = k + 1;
5945       break;
5946     case 2:
5947       *dof = ((k + 1) * (k + 2)) / 2;
5948       break;
5949     case 3:
5950       *dof = ((k + 1) * (k + 2) * (k + 3)) / 6;
5951       break;
5952     default:
5953       *dof = 0;
5954     }
5955   }
5956   PetscFunctionReturn(PETSC_SUCCESS);
5957 }
5958 
5959 /*@
5960   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5961   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5962   section provided (or the section of the `DM`).
5963 
5964   Input Parameters:
5965 + dm      - The `DM`
5966 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5967 - section - The `PetscSection` to reorder, or `NULL` for the default section
5968 
5969   Example:
5970   A typical interpolated single-quad mesh might order points as
5971 .vb
5972   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5973 
5974   v4 -- e6 -- v3
5975   |           |
5976   e7    c0    e8
5977   |           |
5978   v1 -- e5 -- v2
5979 .ve
5980 
5981   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5982   dofs in the order of points, e.g.,
5983 .vb
5984     c0 -> [0,1,2,3]
5985     v1 -> [4]
5986     ...
5987     e5 -> [8, 9]
5988 .ve
5989 
5990   which corresponds to the dofs
5991 .vb
5992     6   10  11  7
5993     13  2   3   15
5994     12  0   1   14
5995     4   8   9   5
5996 .ve
5997 
5998   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5999 .vb
6000   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
6001 .ve
6002 
6003   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
6004 .vb
6005    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
6006 .ve
6007 
6008   Level: developer
6009 
6010   Notes:
6011   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
6012   degree of the basis.
6013 
6014   This is required to run with libCEED.
6015 
6016 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
6017 @*/
6018 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
6019 {
6020   DMLabel   label;
6021   PetscInt  dim, depth = -1, eStart = -1, Nf;
6022   PetscBool continuous = PETSC_TRUE, tensor = PETSC_TRUE;
6023 
6024   PetscFunctionBegin;
6025   PetscCall(DMGetDimension(dm, &dim));
6026   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
6027   if (point < 0) {
6028     PetscInt sStart, sEnd;
6029 
6030     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
6031     point = sEnd - sStart ? sStart : point;
6032   }
6033   PetscCall(DMPlexGetDepthLabel(dm, &label));
6034   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
6035   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6036   if (depth == 1) {
6037     eStart = point;
6038   } else if (depth == dim) {
6039     const PetscInt *cone;
6040 
6041     PetscCall(DMPlexGetCone(dm, point, &cone));
6042     if (dim == 2) eStart = cone[0];
6043     else if (dim == 3) {
6044       const PetscInt *cone2;
6045       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
6046       eStart = cone2[0];
6047     } 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);
6048   } 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);
6049 
6050   PetscCall(PetscSectionGetNumFields(section, &Nf));
6051   for (PetscInt d = 1; d <= dim; d++) {
6052     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
6053     PetscInt *perm;
6054 
6055     for (f = 0; f < Nf; ++f) {
6056       PetscInt dof;
6057 
6058       PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6059       PetscCheck(dim == 1 || tensor || !continuous, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Continuous field %" PetscInt_FMT " must have a tensor product discretization", f);
6060       if (!continuous && d < dim) continue;
6061       PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6062       size += dof * Nc;
6063     }
6064     PetscCall(PetscMalloc1(size, &perm));
6065     for (f = 0; f < Nf; ++f) {
6066       switch (d) {
6067       case 1:
6068         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6069         if (!continuous && d < dim) continue;
6070         /*
6071          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
6072          We want              [ vtx0; edge of length k-1; vtx1 ]
6073          */
6074         if (continuous) {
6075           for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
6076           for (i = 0; i < k - 1; i++)
6077             for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
6078           for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
6079           foffset = offset;
6080         } else {
6081           PetscInt dof;
6082 
6083           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6084           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6085           foffset = offset;
6086         }
6087         break;
6088       case 2:
6089         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
6090         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6091         if (!continuous && d < dim) continue;
6092         /* The SEM order is
6093 
6094          v_lb, {e_b}, v_rb,
6095          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
6096          v_lt, reverse {e_t}, v_rt
6097          */
6098         if (continuous) {
6099           const PetscInt of   = 0;
6100           const PetscInt oeb  = of + PetscSqr(k - 1);
6101           const PetscInt oer  = oeb + (k - 1);
6102           const PetscInt oet  = oer + (k - 1);
6103           const PetscInt oel  = oet + (k - 1);
6104           const PetscInt ovlb = oel + (k - 1);
6105           const PetscInt ovrb = ovlb + 1;
6106           const PetscInt ovrt = ovrb + 1;
6107           const PetscInt ovlt = ovrt + 1;
6108           PetscInt       o;
6109 
6110           /* bottom */
6111           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
6112           for (o = oeb; o < oer; ++o)
6113             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6114           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
6115           /* middle */
6116           for (i = 0; i < k - 1; ++i) {
6117             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
6118             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
6119               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6120             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
6121           }
6122           /* top */
6123           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
6124           for (o = oel - 1; o >= oet; --o)
6125             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6126           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
6127           foffset = offset;
6128         } else {
6129           PetscInt dof;
6130 
6131           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6132           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6133           foffset = offset;
6134         }
6135         break;
6136       case 3:
6137         /* The original hex closure is
6138 
6139          {c,
6140          f_b, f_t, f_f, f_b, f_r, f_l,
6141          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
6142          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
6143          */
6144         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6145         if (!continuous && d < dim) continue;
6146         /* The SEM order is
6147          Bottom Slice
6148          v_blf, {e^{(k-1)-n}_bf}, v_brf,
6149          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
6150          v_blb, {e_bb}, v_brb,
6151 
6152          Middle Slice (j)
6153          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
6154          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
6155          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
6156 
6157          Top Slice
6158          v_tlf, {e_tf}, v_trf,
6159          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
6160          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
6161          */
6162         if (continuous) {
6163           const PetscInt oc    = 0;
6164           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
6165           const PetscInt oft   = ofb + PetscSqr(k - 1);
6166           const PetscInt off   = oft + PetscSqr(k - 1);
6167           const PetscInt ofk   = off + PetscSqr(k - 1);
6168           const PetscInt ofr   = ofk + PetscSqr(k - 1);
6169           const PetscInt ofl   = ofr + PetscSqr(k - 1);
6170           const PetscInt oebl  = ofl + PetscSqr(k - 1);
6171           const PetscInt oebb  = oebl + (k - 1);
6172           const PetscInt oebr  = oebb + (k - 1);
6173           const PetscInt oebf  = oebr + (k - 1);
6174           const PetscInt oetf  = oebf + (k - 1);
6175           const PetscInt oetr  = oetf + (k - 1);
6176           const PetscInt oetb  = oetr + (k - 1);
6177           const PetscInt oetl  = oetb + (k - 1);
6178           const PetscInt oerf  = oetl + (k - 1);
6179           const PetscInt oelf  = oerf + (k - 1);
6180           const PetscInt oelb  = oelf + (k - 1);
6181           const PetscInt oerb  = oelb + (k - 1);
6182           const PetscInt ovblf = oerb + (k - 1);
6183           const PetscInt ovblb = ovblf + 1;
6184           const PetscInt ovbrb = ovblb + 1;
6185           const PetscInt ovbrf = ovbrb + 1;
6186           const PetscInt ovtlf = ovbrf + 1;
6187           const PetscInt ovtrf = ovtlf + 1;
6188           const PetscInt ovtrb = ovtrf + 1;
6189           const PetscInt ovtlb = ovtrb + 1;
6190           PetscInt       o, n;
6191 
6192           /* Bottom Slice */
6193           /*   bottom */
6194           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
6195           for (o = oetf - 1; o >= oebf; --o)
6196             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6197           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
6198           /*   middle */
6199           for (i = 0; i < k - 1; ++i) {
6200             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
6201             for (n = 0; n < k - 1; ++n) {
6202               o = ofb + n * (k - 1) + i;
6203               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6204             }
6205             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
6206           }
6207           /*   top */
6208           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
6209           for (o = oebb; o < oebr; ++o)
6210             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6211           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
6212 
6213           /* Middle Slice */
6214           for (j = 0; j < k - 1; ++j) {
6215             /*   bottom */
6216             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
6217             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
6218               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6219             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
6220             /*   middle */
6221             for (i = 0; i < k - 1; ++i) {
6222               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
6223               for (n = 0; n < k - 1; ++n)
6224                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
6225               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
6226             }
6227             /*   top */
6228             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
6229             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
6230               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6231             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
6232           }
6233 
6234           /* Top Slice */
6235           /*   bottom */
6236           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
6237           for (o = oetf; o < oetr; ++o)
6238             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6239           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
6240           /*   middle */
6241           for (i = 0; i < k - 1; ++i) {
6242             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
6243             for (n = 0; n < k - 1; ++n)
6244               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
6245             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
6246           }
6247           /*   top */
6248           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
6249           for (o = oetl - 1; o >= oetb; --o)
6250             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6251           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
6252 
6253           foffset = offset;
6254         } else {
6255           PetscInt dof;
6256 
6257           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6258           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6259           foffset = offset;
6260         }
6261         break;
6262       default:
6263         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
6264       }
6265     }
6266     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
6267     /* Check permutation */
6268     {
6269       PetscInt *check;
6270 
6271       PetscCall(PetscMalloc1(size, &check));
6272       for (i = 0; i < size; ++i) {
6273         check[i] = -1;
6274         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
6275       }
6276       for (i = 0; i < size; ++i) check[perm[i]] = i;
6277       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
6278       PetscCall(PetscFree(check));
6279     }
6280     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
6281     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
6282       PetscInt *loc_perm;
6283       PetscCall(PetscMalloc1(size * 2, &loc_perm));
6284       for (PetscInt i = 0; i < size; i++) {
6285         loc_perm[i]        = perm[i];
6286         loc_perm[size + i] = size + perm[i];
6287       }
6288       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
6289     }
6290   }
6291   PetscFunctionReturn(PETSC_SUCCESS);
6292 }
6293 
6294 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
6295 {
6296   PetscDS  prob;
6297   PetscInt depth, Nf, h;
6298   DMLabel  label;
6299 
6300   PetscFunctionBeginHot;
6301   PetscCall(DMGetDS(dm, &prob));
6302   Nf      = prob->Nf;
6303   label   = dm->depthLabel;
6304   *dspace = NULL;
6305   if (field < Nf) {
6306     PetscObject disc = prob->disc[field];
6307 
6308     if (disc->classid == PETSCFE_CLASSID) {
6309       PetscDualSpace dsp;
6310 
6311       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
6312       PetscCall(DMLabelGetNumValues(label, &depth));
6313       PetscCall(DMLabelGetValue(label, point, &h));
6314       h = depth - 1 - h;
6315       if (h) {
6316         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
6317       } else {
6318         *dspace = dsp;
6319       }
6320     }
6321   }
6322   PetscFunctionReturn(PETSC_SUCCESS);
6323 }
6324 
6325 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6326 {
6327   PetscScalar       *array;
6328   const PetscScalar *vArray;
6329   const PetscInt    *cone, *coneO;
6330   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
6331 
6332   PetscFunctionBeginHot;
6333   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6334   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6335   PetscCall(DMPlexGetCone(dm, point, &cone));
6336   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6337   if (!values || !*values) {
6338     if ((point >= pStart) && (point < pEnd)) {
6339       PetscInt dof;
6340 
6341       PetscCall(PetscSectionGetDof(section, point, &dof));
6342       size += dof;
6343     }
6344     for (p = 0; p < numPoints; ++p) {
6345       const PetscInt cp = cone[p];
6346       PetscInt       dof;
6347 
6348       if ((cp < pStart) || (cp >= pEnd)) continue;
6349       PetscCall(PetscSectionGetDof(section, cp, &dof));
6350       size += dof;
6351     }
6352     if (!values) {
6353       if (csize) *csize = size;
6354       PetscFunctionReturn(PETSC_SUCCESS);
6355     }
6356     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
6357   } else {
6358     array = *values;
6359   }
6360   size = 0;
6361   PetscCall(VecGetArrayRead(v, &vArray));
6362   if ((point >= pStart) && (point < pEnd)) {
6363     PetscInt           dof, off, d;
6364     const PetscScalar *varr;
6365 
6366     PetscCall(PetscSectionGetDof(section, point, &dof));
6367     PetscCall(PetscSectionGetOffset(section, point, &off));
6368     varr = PetscSafePointerPlusOffset(vArray, off);
6369     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6370     size += dof;
6371   }
6372   for (p = 0; p < numPoints; ++p) {
6373     const PetscInt     cp = cone[p];
6374     PetscInt           o  = coneO[p];
6375     PetscInt           dof, off, d;
6376     const PetscScalar *varr;
6377 
6378     if ((cp < pStart) || (cp >= pEnd)) continue;
6379     PetscCall(PetscSectionGetDof(section, cp, &dof));
6380     PetscCall(PetscSectionGetOffset(section, cp, &off));
6381     varr = PetscSafePointerPlusOffset(vArray, off);
6382     if (o >= 0) {
6383       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6384     } else {
6385       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
6386     }
6387     size += dof;
6388   }
6389   PetscCall(VecRestoreArrayRead(v, &vArray));
6390   if (!*values) {
6391     if (csize) *csize = size;
6392     *values = array;
6393   } else {
6394     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6395     *csize = size;
6396   }
6397   PetscFunctionReturn(PETSC_SUCCESS);
6398 }
6399 
6400 /* Compress out points not in the section */
6401 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
6402 {
6403   const PetscInt np = *numPoints;
6404   PetscInt       pStart, pEnd, p, q;
6405 
6406   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6407   for (p = 0, q = 0; p < np; ++p) {
6408     const PetscInt r = points[p * 2];
6409     if ((r >= pStart) && (r < pEnd)) {
6410       points[q * 2]     = r;
6411       points[q * 2 + 1] = points[p * 2 + 1];
6412       ++q;
6413     }
6414   }
6415   *numPoints = q;
6416   return PETSC_SUCCESS;
6417 }
6418 
6419 /* Compressed closure does not apply closure permutation */
6420 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6421 {
6422   const PetscInt *cla = NULL;
6423   PetscInt        np, *pts = NULL;
6424 
6425   PetscFunctionBeginHot;
6426   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
6427   if (!ornt && *clPoints) {
6428     PetscInt dof, off;
6429 
6430     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
6431     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
6432     PetscCall(ISGetIndices(*clPoints, &cla));
6433     np  = dof / 2;
6434     pts = PetscSafePointerPlusOffset((PetscInt *)cla, off);
6435   } else {
6436     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
6437     PetscCall(CompressPoints_Private(section, &np, pts));
6438   }
6439   *numPoints = np;
6440   *points    = pts;
6441   *clp       = cla;
6442   PetscFunctionReturn(PETSC_SUCCESS);
6443 }
6444 
6445 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6446 {
6447   PetscFunctionBeginHot;
6448   if (!*clPoints) {
6449     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
6450   } else {
6451     PetscCall(ISRestoreIndices(*clPoints, clp));
6452   }
6453   *numPoints = 0;
6454   *points    = NULL;
6455   *clSec     = NULL;
6456   *clPoints  = NULL;
6457   *clp       = NULL;
6458   PetscFunctionReturn(PETSC_SUCCESS);
6459 }
6460 
6461 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
6462 {
6463   PetscInt            offset = 0, p;
6464   const PetscInt    **perms  = NULL;
6465   const PetscScalar **flips  = NULL;
6466 
6467   PetscFunctionBeginHot;
6468   *size = 0;
6469   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6470   for (p = 0; p < numPoints; p++) {
6471     const PetscInt     point = points[2 * p];
6472     const PetscInt    *perm  = perms ? perms[p] : NULL;
6473     const PetscScalar *flip  = flips ? flips[p] : NULL;
6474     PetscInt           dof, off, d;
6475     const PetscScalar *varr;
6476 
6477     PetscCall(PetscSectionGetDof(section, point, &dof));
6478     PetscCall(PetscSectionGetOffset(section, point, &off));
6479     varr = PetscSafePointerPlusOffset(vArray, off);
6480     if (clperm) {
6481       if (perm) {
6482         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
6483       } else {
6484         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
6485       }
6486       if (flip) {
6487         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
6488       }
6489     } else {
6490       if (perm) {
6491         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
6492       } else {
6493         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
6494       }
6495       if (flip) {
6496         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
6497       }
6498     }
6499     offset += dof;
6500   }
6501   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6502   *size = offset;
6503   PetscFunctionReturn(PETSC_SUCCESS);
6504 }
6505 
6506 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[])
6507 {
6508   PetscInt offset = 0, f;
6509 
6510   PetscFunctionBeginHot;
6511   *size = 0;
6512   for (f = 0; f < numFields; ++f) {
6513     PetscInt            p;
6514     const PetscInt    **perms = NULL;
6515     const PetscScalar **flips = NULL;
6516 
6517     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6518     for (p = 0; p < numPoints; p++) {
6519       const PetscInt     point = points[2 * p];
6520       PetscInt           fdof, foff, b;
6521       const PetscScalar *varr;
6522       const PetscInt    *perm = perms ? perms[p] : NULL;
6523       const PetscScalar *flip = flips ? flips[p] : NULL;
6524 
6525       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6526       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6527       varr = &vArray[foff];
6528       if (clperm) {
6529         if (perm) {
6530           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6531         } else {
6532           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6533         }
6534         if (flip) {
6535           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6536         }
6537       } else {
6538         if (perm) {
6539           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6540         } else {
6541           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6542         }
6543         if (flip) {
6544           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6545         }
6546       }
6547       offset += fdof;
6548     }
6549     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6550   }
6551   *size = offset;
6552   PetscFunctionReturn(PETSC_SUCCESS);
6553 }
6554 
6555 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, PetscBool useClPerm, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6556 {
6557   PetscSection    clSection;
6558   IS              clPoints;
6559   PetscInt       *points = NULL;
6560   const PetscInt *clp, *perm = NULL;
6561   PetscInt        depth, numFields, numPoints, asize;
6562 
6563   PetscFunctionBeginHot;
6564   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6565   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6566   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6567   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
6568   PetscCall(DMPlexGetDepth(dm, &depth));
6569   PetscCall(PetscSectionGetNumFields(section, &numFields));
6570   if (depth == 1 && numFields < 2) {
6571     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6572     PetscFunctionReturn(PETSC_SUCCESS);
6573   }
6574   /* Get points */
6575   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6576   /* Get sizes */
6577   asize = 0;
6578   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6579     PetscInt dof;
6580     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6581     asize += dof;
6582   }
6583   if (values) {
6584     const PetscScalar *vArray;
6585     PetscInt           size;
6586 
6587     if (*values) {
6588       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);
6589     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6590     if (useClPerm) PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6591     PetscCall(VecGetArrayRead(v, &vArray));
6592     /* Get values */
6593     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6594     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6595     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6596     /* Cleanup array */
6597     PetscCall(VecRestoreArrayRead(v, &vArray));
6598   }
6599   if (csize) *csize = asize;
6600   /* Cleanup points */
6601   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6602   PetscFunctionReturn(PETSC_SUCCESS);
6603 }
6604 
6605 /*@C
6606   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6607 
6608   Not collective
6609 
6610   Input Parameters:
6611 + dm      - The `DM`
6612 . section - The section describing the layout in `v`, or `NULL` to use the default section
6613 . v       - The local vector
6614 - point   - The point in the `DM`
6615 
6616   Input/Output Parameters:
6617 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6618 - values - An array to use for the values, or *values = `NULL` to have it allocated automatically;
6619            if the user provided `NULL`, it is a borrowed array and should not be freed, use  `DMPlexVecRestoreClosure()` to return it
6620 
6621   Level: intermediate
6622 
6623   Notes:
6624   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6625   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6626   assembly function, and a user may already have allocated storage for this operation.
6627 
6628   A typical use could be
6629 .vb
6630    values = NULL;
6631    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6632    for (cl = 0; cl < clSize; ++cl) {
6633      <Compute on closure>
6634    }
6635    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6636 .ve
6637   or
6638 .vb
6639    PetscMalloc1(clMaxSize, &values);
6640    for (p = pStart; p < pEnd; ++p) {
6641      clSize = clMaxSize;
6642      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6643      for (cl = 0; cl < clSize; ++cl) {
6644        <Compute on closure>
6645      }
6646    }
6647    PetscFree(values);
6648 .ve
6649 
6650   Fortran Notes:
6651   The `csize` argument is present in the Fortran binding. Since the Fortran `values` array contains its length information this argument may not be needed.
6652   In that case one may pass `PETSC_NULL_INTEGER` for `csize`.
6653 
6654   `values` must be declared with
6655 .vb
6656   PetscScalar,dimension(:),pointer   :: values
6657 .ve
6658   and it will be allocated internally by PETSc to hold the values returned
6659 
6660 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6661 @*/
6662 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6663 {
6664   PetscFunctionBeginHot;
6665   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6666   PetscFunctionReturn(PETSC_SUCCESS);
6667 }
6668 
6669 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6670 {
6671   DMLabel            depthLabel;
6672   PetscSection       clSection;
6673   IS                 clPoints;
6674   PetscScalar       *array;
6675   const PetscScalar *vArray;
6676   PetscInt          *points = NULL;
6677   const PetscInt    *clp, *perm = NULL;
6678   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6679 
6680   PetscFunctionBeginHot;
6681   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6682   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6683   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6684   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6685   PetscCall(DMPlexGetDepth(dm, &mdepth));
6686   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6687   PetscCall(PetscSectionGetNumFields(section, &numFields));
6688   if (mdepth == 1 && numFields < 2) {
6689     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6690     PetscFunctionReturn(PETSC_SUCCESS);
6691   }
6692   /* Get points */
6693   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6694   for (clsize = 0, p = 0; p < Np; p++) {
6695     PetscInt dof;
6696     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6697     clsize += dof;
6698   }
6699   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6700   /* Filter points */
6701   for (p = 0; p < numPoints * 2; p += 2) {
6702     PetscInt dep;
6703 
6704     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6705     if (dep != depth) continue;
6706     points[Np * 2 + 0] = points[p];
6707     points[Np * 2 + 1] = points[p + 1];
6708     ++Np;
6709   }
6710   /* Get array */
6711   if (!values || !*values) {
6712     PetscInt asize = 0, dof;
6713 
6714     for (p = 0; p < Np * 2; p += 2) {
6715       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6716       asize += dof;
6717     }
6718     if (!values) {
6719       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6720       if (csize) *csize = asize;
6721       PetscFunctionReturn(PETSC_SUCCESS);
6722     }
6723     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6724   } else {
6725     array = *values;
6726   }
6727   PetscCall(VecGetArrayRead(v, &vArray));
6728   /* Get values */
6729   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6730   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6731   /* Cleanup points */
6732   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6733   /* Cleanup array */
6734   PetscCall(VecRestoreArrayRead(v, &vArray));
6735   if (!*values) {
6736     if (csize) *csize = size;
6737     *values = array;
6738   } else {
6739     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6740     *csize = size;
6741   }
6742   PetscFunctionReturn(PETSC_SUCCESS);
6743 }
6744 
6745 /*@C
6746   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point' obtained with `DMPlexVecGetClosure()`
6747 
6748   Not collective
6749 
6750   Input Parameters:
6751 + dm      - The `DM`
6752 . section - The section describing the layout in `v`, or `NULL` to use the default section
6753 . v       - The local vector
6754 . point   - The point in the `DM`
6755 . csize   - The number of values in the closure, or `NULL`
6756 - values  - The array of values
6757 
6758   Level: intermediate
6759 
6760   Note:
6761   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6762 
6763   Fortran Note:
6764   The `csize` argument is present in the Fortran binding. Since the Fortran `values` array contains its length information this argument may not be needed.
6765   In that case one may pass `PETSC_NULL_INTEGER` for `csize`.
6766 
6767 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6768 @*/
6769 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6770 {
6771   PetscInt size = 0;
6772 
6773   PetscFunctionBegin;
6774   /* Should work without recalculating size */
6775   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6776   *values = NULL;
6777   PetscFunctionReturn(PETSC_SUCCESS);
6778 }
6779 
6780 static inline void add(PetscScalar *x, PetscScalar y)
6781 {
6782   *x += y;
6783 }
6784 static inline void insert(PetscScalar *x, PetscScalar y)
6785 {
6786   *x = y;
6787 }
6788 
6789 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[])
6790 {
6791   PetscInt        cdof;  /* The number of constraints on this point */
6792   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6793   PetscScalar    *a;
6794   PetscInt        off, cind = 0, k;
6795 
6796   PetscFunctionBegin;
6797   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6798   PetscCall(PetscSectionGetOffset(section, point, &off));
6799   a = &array[off];
6800   if (!cdof || setBC) {
6801     if (clperm) {
6802       if (perm) {
6803         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6804       } else {
6805         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6806       }
6807     } else {
6808       if (perm) {
6809         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6810       } else {
6811         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6812       }
6813     }
6814   } else {
6815     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6816     if (clperm) {
6817       if (perm) {
6818         for (k = 0; k < dof; ++k) {
6819           if ((cind < cdof) && (k == cdofs[cind])) {
6820             ++cind;
6821             continue;
6822           }
6823           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6824         }
6825       } else {
6826         for (k = 0; k < dof; ++k) {
6827           if ((cind < cdof) && (k == cdofs[cind])) {
6828             ++cind;
6829             continue;
6830           }
6831           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6832         }
6833       }
6834     } else {
6835       if (perm) {
6836         for (k = 0; k < dof; ++k) {
6837           if ((cind < cdof) && (k == cdofs[cind])) {
6838             ++cind;
6839             continue;
6840           }
6841           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6842         }
6843       } else {
6844         for (k = 0; k < dof; ++k) {
6845           if ((cind < cdof) && (k == cdofs[cind])) {
6846             ++cind;
6847             continue;
6848           }
6849           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6850         }
6851       }
6852     }
6853   }
6854   PetscFunctionReturn(PETSC_SUCCESS);
6855 }
6856 
6857 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[])
6858 {
6859   PetscInt        cdof;  /* The number of constraints on this point */
6860   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6861   PetscScalar    *a;
6862   PetscInt        off, cind = 0, k;
6863 
6864   PetscFunctionBegin;
6865   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6866   PetscCall(PetscSectionGetOffset(section, point, &off));
6867   a = &array[off];
6868   if (cdof) {
6869     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6870     if (clperm) {
6871       if (perm) {
6872         for (k = 0; k < dof; ++k) {
6873           if ((cind < cdof) && (k == cdofs[cind])) {
6874             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6875             cind++;
6876           }
6877         }
6878       } else {
6879         for (k = 0; k < dof; ++k) {
6880           if ((cind < cdof) && (k == cdofs[cind])) {
6881             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6882             cind++;
6883           }
6884         }
6885       }
6886     } else {
6887       if (perm) {
6888         for (k = 0; k < dof; ++k) {
6889           if ((cind < cdof) && (k == cdofs[cind])) {
6890             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6891             cind++;
6892           }
6893         }
6894       } else {
6895         for (k = 0; k < dof; ++k) {
6896           if ((cind < cdof) && (k == cdofs[cind])) {
6897             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6898             cind++;
6899           }
6900         }
6901       }
6902     }
6903   }
6904   PetscFunctionReturn(PETSC_SUCCESS);
6905 }
6906 
6907 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[])
6908 {
6909   PetscScalar    *a;
6910   PetscInt        fdof, foff, fcdof, foffset = *offset;
6911   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6912   PetscInt        cind = 0, b;
6913 
6914   PetscFunctionBegin;
6915   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6916   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6917   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6918   a = &array[foff];
6919   if (!fcdof || setBC) {
6920     if (clperm) {
6921       if (perm) {
6922         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6923       } else {
6924         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6925       }
6926     } else {
6927       if (perm) {
6928         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6929       } else {
6930         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6931       }
6932     }
6933   } else {
6934     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6935     if (clperm) {
6936       if (perm) {
6937         for (b = 0; b < fdof; b++) {
6938           if ((cind < fcdof) && (b == fcdofs[cind])) {
6939             ++cind;
6940             continue;
6941           }
6942           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6943         }
6944       } else {
6945         for (b = 0; b < fdof; b++) {
6946           if ((cind < fcdof) && (b == fcdofs[cind])) {
6947             ++cind;
6948             continue;
6949           }
6950           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6951         }
6952       }
6953     } else {
6954       if (perm) {
6955         for (b = 0; b < fdof; b++) {
6956           if ((cind < fcdof) && (b == fcdofs[cind])) {
6957             ++cind;
6958             continue;
6959           }
6960           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6961         }
6962       } else {
6963         for (b = 0; b < fdof; b++) {
6964           if ((cind < fcdof) && (b == fcdofs[cind])) {
6965             ++cind;
6966             continue;
6967           }
6968           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6969         }
6970       }
6971     }
6972   }
6973   *offset += fdof;
6974   PetscFunctionReturn(PETSC_SUCCESS);
6975 }
6976 
6977 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[])
6978 {
6979   PetscScalar    *a;
6980   PetscInt        fdof, foff, fcdof, foffset = *offset;
6981   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6982   PetscInt        Nc, cind = 0, ncind = 0, b;
6983   PetscBool       ncSet, fcSet;
6984 
6985   PetscFunctionBegin;
6986   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6987   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6988   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6989   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6990   a = &array[foff];
6991   if (fcdof) {
6992     /* We just override fcdof and fcdofs with Ncc and comps */
6993     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6994     if (clperm) {
6995       if (perm) {
6996         if (comps) {
6997           for (b = 0; b < fdof; b++) {
6998             ncSet = fcSet = PETSC_FALSE;
6999             if (b % Nc == comps[ncind]) {
7000               ncind = (ncind + 1) % Ncc;
7001               ncSet = PETSC_TRUE;
7002             }
7003             if ((cind < fcdof) && (b == fcdofs[cind])) {
7004               ++cind;
7005               fcSet = PETSC_TRUE;
7006             }
7007             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
7008           }
7009         } else {
7010           for (b = 0; b < fdof; b++) {
7011             if ((cind < fcdof) && (b == fcdofs[cind])) {
7012               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
7013               ++cind;
7014             }
7015           }
7016         }
7017       } else {
7018         if (comps) {
7019           for (b = 0; b < fdof; b++) {
7020             ncSet = fcSet = PETSC_FALSE;
7021             if (b % Nc == comps[ncind]) {
7022               ncind = (ncind + 1) % Ncc;
7023               ncSet = PETSC_TRUE;
7024             }
7025             if ((cind < fcdof) && (b == fcdofs[cind])) {
7026               ++cind;
7027               fcSet = PETSC_TRUE;
7028             }
7029             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
7030           }
7031         } else {
7032           for (b = 0; b < fdof; b++) {
7033             if ((cind < fcdof) && (b == fcdofs[cind])) {
7034               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
7035               ++cind;
7036             }
7037           }
7038         }
7039       }
7040     } else {
7041       if (perm) {
7042         if (comps) {
7043           for (b = 0; b < fdof; b++) {
7044             ncSet = fcSet = PETSC_FALSE;
7045             if (b % Nc == comps[ncind]) {
7046               ncind = (ncind + 1) % Ncc;
7047               ncSet = PETSC_TRUE;
7048             }
7049             if ((cind < fcdof) && (b == fcdofs[cind])) {
7050               ++cind;
7051               fcSet = PETSC_TRUE;
7052             }
7053             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
7054           }
7055         } else {
7056           for (b = 0; b < fdof; b++) {
7057             if ((cind < fcdof) && (b == fcdofs[cind])) {
7058               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
7059               ++cind;
7060             }
7061           }
7062         }
7063       } else {
7064         if (comps) {
7065           for (b = 0; b < fdof; b++) {
7066             ncSet = fcSet = PETSC_FALSE;
7067             if (b % Nc == comps[ncind]) {
7068               ncind = (ncind + 1) % Ncc;
7069               ncSet = PETSC_TRUE;
7070             }
7071             if ((cind < fcdof) && (b == fcdofs[cind])) {
7072               ++cind;
7073               fcSet = PETSC_TRUE;
7074             }
7075             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
7076           }
7077         } else {
7078           for (b = 0; b < fdof; b++) {
7079             if ((cind < fcdof) && (b == fcdofs[cind])) {
7080               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
7081               ++cind;
7082             }
7083           }
7084         }
7085       }
7086     }
7087   }
7088   *offset += fdof;
7089   PetscFunctionReturn(PETSC_SUCCESS);
7090 }
7091 
7092 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7093 {
7094   PetscScalar    *array;
7095   const PetscInt *cone, *coneO;
7096   PetscInt        pStart, pEnd, p, numPoints, off, dof;
7097 
7098   PetscFunctionBeginHot;
7099   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
7100   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
7101   PetscCall(DMPlexGetCone(dm, point, &cone));
7102   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
7103   PetscCall(VecGetArray(v, &array));
7104   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
7105     const PetscInt cp = !p ? point : cone[p - 1];
7106     const PetscInt o  = !p ? 0 : coneO[p - 1];
7107 
7108     if ((cp < pStart) || (cp >= pEnd)) {
7109       dof = 0;
7110       continue;
7111     }
7112     PetscCall(PetscSectionGetDof(section, cp, &dof));
7113     /* ADD_VALUES */
7114     {
7115       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7116       PetscScalar    *a;
7117       PetscInt        cdof, coff, cind = 0, k;
7118 
7119       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
7120       PetscCall(PetscSectionGetOffset(section, cp, &coff));
7121       a = &array[coff];
7122       if (!cdof) {
7123         if (o >= 0) {
7124           for (k = 0; k < dof; ++k) a[k] += values[off + k];
7125         } else {
7126           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
7127         }
7128       } else {
7129         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
7130         if (o >= 0) {
7131           for (k = 0; k < dof; ++k) {
7132             if ((cind < cdof) && (k == cdofs[cind])) {
7133               ++cind;
7134               continue;
7135             }
7136             a[k] += values[off + k];
7137           }
7138         } else {
7139           for (k = 0; k < dof; ++k) {
7140             if ((cind < cdof) && (k == cdofs[cind])) {
7141               ++cind;
7142               continue;
7143             }
7144             a[k] += values[off + dof - k - 1];
7145           }
7146         }
7147       }
7148     }
7149   }
7150   PetscCall(VecRestoreArray(v, &array));
7151   PetscFunctionReturn(PETSC_SUCCESS);
7152 }
7153 
7154 /*@C
7155   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
7156 
7157   Not collective
7158 
7159   Input Parameters:
7160 + dm      - The `DM`
7161 . section - The section describing the layout in `v`, or `NULL` to use the default section
7162 . v       - The local vector
7163 . point   - The point in the `DM`
7164 . values  - The array of values
7165 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
7166             where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
7167 
7168   Level: intermediate
7169 
7170   Note:
7171   Usually the input arrays were obtained with `DMPlexVecGetClosure()`
7172 
7173 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
7174 @*/
7175 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7176 {
7177   PetscSection    clSection;
7178   IS              clPoints;
7179   PetscScalar    *array;
7180   PetscInt       *points = NULL;
7181   const PetscInt *clp, *clperm = NULL;
7182   PetscInt        depth, numFields, numPoints, p, clsize;
7183 
7184   PetscFunctionBeginHot;
7185   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7186   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7187   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7188   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7189   PetscCall(DMPlexGetDepth(dm, &depth));
7190   PetscCall(PetscSectionGetNumFields(section, &numFields));
7191   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
7192     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
7193     PetscFunctionReturn(PETSC_SUCCESS);
7194   }
7195   /* Get points */
7196   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7197   for (clsize = 0, p = 0; p < numPoints; p++) {
7198     PetscInt dof;
7199     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7200     clsize += dof;
7201   }
7202   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7203   /* Get array */
7204   PetscCall(VecGetArray(v, &array));
7205   /* Get values */
7206   if (numFields > 0) {
7207     PetscInt offset = 0, f;
7208     for (f = 0; f < numFields; ++f) {
7209       const PetscInt    **perms = NULL;
7210       const PetscScalar **flips = NULL;
7211 
7212       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7213       switch (mode) {
7214       case INSERT_VALUES:
7215         for (p = 0; p < numPoints; p++) {
7216           const PetscInt     point = points[2 * p];
7217           const PetscInt    *perm  = perms ? perms[p] : NULL;
7218           const PetscScalar *flip  = flips ? flips[p] : NULL;
7219           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
7220         }
7221         break;
7222       case INSERT_ALL_VALUES:
7223         for (p = 0; p < numPoints; p++) {
7224           const PetscInt     point = points[2 * p];
7225           const PetscInt    *perm  = perms ? perms[p] : NULL;
7226           const PetscScalar *flip  = flips ? flips[p] : NULL;
7227           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
7228         }
7229         break;
7230       case INSERT_BC_VALUES:
7231         for (p = 0; p < numPoints; p++) {
7232           const PetscInt     point = points[2 * p];
7233           const PetscInt    *perm  = perms ? perms[p] : NULL;
7234           const PetscScalar *flip  = flips ? flips[p] : NULL;
7235           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
7236         }
7237         break;
7238       case ADD_VALUES:
7239         for (p = 0; p < numPoints; p++) {
7240           const PetscInt     point = points[2 * p];
7241           const PetscInt    *perm  = perms ? perms[p] : NULL;
7242           const PetscScalar *flip  = flips ? flips[p] : NULL;
7243           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
7244         }
7245         break;
7246       case ADD_ALL_VALUES:
7247         for (p = 0; p < numPoints; p++) {
7248           const PetscInt     point = points[2 * p];
7249           const PetscInt    *perm  = perms ? perms[p] : NULL;
7250           const PetscScalar *flip  = flips ? flips[p] : NULL;
7251           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
7252         }
7253         break;
7254       case ADD_BC_VALUES:
7255         for (p = 0; p < numPoints; p++) {
7256           const PetscInt     point = points[2 * p];
7257           const PetscInt    *perm  = perms ? perms[p] : NULL;
7258           const PetscScalar *flip  = flips ? flips[p] : NULL;
7259           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
7260         }
7261         break;
7262       default:
7263         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7264       }
7265       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7266     }
7267   } else {
7268     PetscInt            dof, off;
7269     const PetscInt    **perms = NULL;
7270     const PetscScalar **flips = NULL;
7271 
7272     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
7273     switch (mode) {
7274     case INSERT_VALUES:
7275       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7276         const PetscInt     point = points[2 * p];
7277         const PetscInt    *perm  = perms ? perms[p] : NULL;
7278         const PetscScalar *flip  = flips ? flips[p] : NULL;
7279         PetscCall(PetscSectionGetDof(section, point, &dof));
7280         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
7281       }
7282       break;
7283     case INSERT_ALL_VALUES:
7284       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7285         const PetscInt     point = points[2 * p];
7286         const PetscInt    *perm  = perms ? perms[p] : NULL;
7287         const PetscScalar *flip  = flips ? flips[p] : NULL;
7288         PetscCall(PetscSectionGetDof(section, point, &dof));
7289         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
7290       }
7291       break;
7292     case INSERT_BC_VALUES:
7293       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7294         const PetscInt     point = points[2 * p];
7295         const PetscInt    *perm  = perms ? perms[p] : NULL;
7296         const PetscScalar *flip  = flips ? flips[p] : NULL;
7297         PetscCall(PetscSectionGetDof(section, point, &dof));
7298         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
7299       }
7300       break;
7301     case ADD_VALUES:
7302       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7303         const PetscInt     point = points[2 * p];
7304         const PetscInt    *perm  = perms ? perms[p] : NULL;
7305         const PetscScalar *flip  = flips ? flips[p] : NULL;
7306         PetscCall(PetscSectionGetDof(section, point, &dof));
7307         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
7308       }
7309       break;
7310     case ADD_ALL_VALUES:
7311       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7312         const PetscInt     point = points[2 * p];
7313         const PetscInt    *perm  = perms ? perms[p] : NULL;
7314         const PetscScalar *flip  = flips ? flips[p] : NULL;
7315         PetscCall(PetscSectionGetDof(section, point, &dof));
7316         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
7317       }
7318       break;
7319     case ADD_BC_VALUES:
7320       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7321         const PetscInt     point = points[2 * p];
7322         const PetscInt    *perm  = perms ? perms[p] : NULL;
7323         const PetscScalar *flip  = flips ? flips[p] : NULL;
7324         PetscCall(PetscSectionGetDof(section, point, &dof));
7325         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
7326       }
7327       break;
7328     default:
7329       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7330     }
7331     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
7332   }
7333   /* Cleanup points */
7334   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7335   /* Cleanup array */
7336   PetscCall(VecRestoreArray(v, &array));
7337   PetscFunctionReturn(PETSC_SUCCESS);
7338 }
7339 
7340 /* Check whether the given point is in the label. If not, update the offset to skip this point */
7341 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
7342 {
7343   PetscFunctionBegin;
7344   *contains = PETSC_TRUE;
7345   if (label) {
7346     PetscInt fdof;
7347 
7348     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
7349     if (!*contains) {
7350       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7351       *offset += fdof;
7352       PetscFunctionReturn(PETSC_SUCCESS);
7353     }
7354   }
7355   PetscFunctionReturn(PETSC_SUCCESS);
7356 }
7357 
7358 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
7359 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)
7360 {
7361   PetscSection    clSection;
7362   IS              clPoints;
7363   PetscScalar    *array;
7364   PetscInt       *points = NULL;
7365   const PetscInt *clp;
7366   PetscInt        numFields, numPoints, p;
7367   PetscInt        offset = 0, f;
7368 
7369   PetscFunctionBeginHot;
7370   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7371   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7372   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7373   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7374   PetscCall(PetscSectionGetNumFields(section, &numFields));
7375   /* Get points */
7376   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7377   /* Get array */
7378   PetscCall(VecGetArray(v, &array));
7379   /* Get values */
7380   for (f = 0; f < numFields; ++f) {
7381     const PetscInt    **perms = NULL;
7382     const PetscScalar **flips = NULL;
7383     PetscBool           contains;
7384 
7385     if (!fieldActive[f]) {
7386       for (p = 0; p < numPoints * 2; p += 2) {
7387         PetscInt fdof;
7388         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7389         offset += fdof;
7390       }
7391       continue;
7392     }
7393     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7394     switch (mode) {
7395     case INSERT_VALUES:
7396       for (p = 0; p < numPoints; p++) {
7397         const PetscInt     point = points[2 * p];
7398         const PetscInt    *perm  = perms ? perms[p] : NULL;
7399         const PetscScalar *flip  = flips ? flips[p] : NULL;
7400         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7401         if (!contains) continue;
7402         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
7403       }
7404       break;
7405     case INSERT_ALL_VALUES:
7406       for (p = 0; p < numPoints; p++) {
7407         const PetscInt     point = points[2 * p];
7408         const PetscInt    *perm  = perms ? perms[p] : NULL;
7409         const PetscScalar *flip  = flips ? flips[p] : NULL;
7410         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7411         if (!contains) continue;
7412         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
7413       }
7414       break;
7415     case INSERT_BC_VALUES:
7416       for (p = 0; p < numPoints; p++) {
7417         const PetscInt     point = points[2 * p];
7418         const PetscInt    *perm  = perms ? perms[p] : NULL;
7419         const PetscScalar *flip  = flips ? flips[p] : NULL;
7420         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7421         if (!contains) continue;
7422         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
7423       }
7424       break;
7425     case ADD_VALUES:
7426       for (p = 0; p < numPoints; p++) {
7427         const PetscInt     point = points[2 * p];
7428         const PetscInt    *perm  = perms ? perms[p] : NULL;
7429         const PetscScalar *flip  = flips ? flips[p] : NULL;
7430         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7431         if (!contains) continue;
7432         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
7433       }
7434       break;
7435     case ADD_ALL_VALUES:
7436       for (p = 0; p < numPoints; p++) {
7437         const PetscInt     point = points[2 * p];
7438         const PetscInt    *perm  = perms ? perms[p] : NULL;
7439         const PetscScalar *flip  = flips ? flips[p] : NULL;
7440         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7441         if (!contains) continue;
7442         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
7443       }
7444       break;
7445     default:
7446       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7447     }
7448     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7449   }
7450   /* Cleanup points */
7451   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7452   /* Cleanup array */
7453   PetscCall(VecRestoreArray(v, &array));
7454   PetscFunctionReturn(PETSC_SUCCESS);
7455 }
7456 
7457 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
7458 {
7459   PetscMPIInt rank;
7460   PetscInt    i, j;
7461 
7462   PetscFunctionBegin;
7463   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7464   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
7465   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
7466   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
7467   numCIndices = numCIndices ? numCIndices : numRIndices;
7468   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
7469   for (i = 0; i < numRIndices; i++) {
7470     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
7471     for (j = 0; j < numCIndices; j++) {
7472 #if defined(PETSC_USE_COMPLEX)
7473       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
7474 #else
7475       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
7476 #endif
7477     }
7478     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
7479   }
7480   PetscFunctionReturn(PETSC_SUCCESS);
7481 }
7482 
7483 /*
7484   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
7485 
7486   Input Parameters:
7487 + section - The section for this data layout
7488 . islocal - Is the section (and thus indices being requested) local or global?
7489 . point   - The point contributing dofs with these indices
7490 . off     - The global offset of this point
7491 . loff    - The local offset of each field
7492 . setBC   - The flag determining whether to include indices of boundary values
7493 . perm    - A permutation of the dofs on this point, or NULL
7494 - indperm - A permutation of the entire indices array, or NULL
7495 
7496   Output Parameter:
7497 . indices - Indices for dofs on this point
7498 
7499   Level: developer
7500 
7501   Note: The indices could be local or global, depending on the value of 'off'.
7502 */
7503 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7504 {
7505   PetscInt        dof;   /* The number of unknowns on this point */
7506   PetscInt        cdof;  /* The number of constraints on this point */
7507   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7508   PetscInt        cind = 0, k;
7509 
7510   PetscFunctionBegin;
7511   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7512   PetscCall(PetscSectionGetDof(section, point, &dof));
7513   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7514   if (!cdof || setBC) {
7515     for (k = 0; k < dof; ++k) {
7516       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7517       const PetscInt ind    = indperm ? indperm[preind] : preind;
7518 
7519       indices[ind] = off + k;
7520     }
7521   } else {
7522     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7523     for (k = 0; k < dof; ++k) {
7524       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7525       const PetscInt ind    = indperm ? indperm[preind] : preind;
7526 
7527       if ((cind < cdof) && (k == cdofs[cind])) {
7528         /* Insert check for returning constrained indices */
7529         indices[ind] = -(off + k + 1);
7530         ++cind;
7531       } else {
7532         indices[ind] = off + k - (islocal ? 0 : cind);
7533       }
7534     }
7535   }
7536   *loff += dof;
7537   PetscFunctionReturn(PETSC_SUCCESS);
7538 }
7539 
7540 /*
7541  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7542 
7543  Input Parameters:
7544 + section - a section (global or local)
7545 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7546 . point - point within section
7547 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7548 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7549 . setBC - identify constrained (boundary condition) points via involution.
7550 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7551 . permsoff - offset
7552 - indperm - index permutation
7553 
7554  Output Parameter:
7555 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7556 . indices - array to hold indices (as defined by section) of each dof associated with point
7557 
7558  Notes:
7559  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7560  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7561  in the local vector.
7562 
7563  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7564  significant).  It is invalid to call with a global section and setBC=true.
7565 
7566  Developer Note:
7567  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7568  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7569  offset could be obtained from the section instead of passing it explicitly as we do now.
7570 
7571  Example:
7572  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7573  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7574  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7575  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.
7576 
7577  Level: developer
7578 */
7579 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[])
7580 {
7581   PetscInt numFields, foff, f;
7582 
7583   PetscFunctionBegin;
7584   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7585   PetscCall(PetscSectionGetNumFields(section, &numFields));
7586   for (f = 0, foff = 0; f < numFields; ++f) {
7587     PetscInt        fdof, cfdof;
7588     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7589     PetscInt        cind = 0, b;
7590     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7591 
7592     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7593     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7594     if (!cfdof || setBC) {
7595       for (b = 0; b < fdof; ++b) {
7596         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7597         const PetscInt ind    = indperm ? indperm[preind] : preind;
7598 
7599         indices[ind] = off + foff + b;
7600       }
7601     } else {
7602       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7603       for (b = 0; b < fdof; ++b) {
7604         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7605         const PetscInt ind    = indperm ? indperm[preind] : preind;
7606 
7607         if ((cind < cfdof) && (b == fcdofs[cind])) {
7608           indices[ind] = -(off + foff + b + 1);
7609           ++cind;
7610         } else {
7611           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7612         }
7613       }
7614     }
7615     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7616     foffs[f] += fdof;
7617   }
7618   PetscFunctionReturn(PETSC_SUCCESS);
7619 }
7620 
7621 /*
7622   This version believes the globalSection offsets for each field, rather than just the point offset
7623 
7624  . foffs - The offset into 'indices' for each field, since it is segregated by field
7625 
7626  Notes:
7627  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7628  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7629 */
7630 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7631 {
7632   PetscInt numFields, foff, f;
7633 
7634   PetscFunctionBegin;
7635   PetscCall(PetscSectionGetNumFields(section, &numFields));
7636   for (f = 0; f < numFields; ++f) {
7637     PetscInt        fdof, cfdof;
7638     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7639     PetscInt        cind = 0, b;
7640     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7641 
7642     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7643     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7644     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7645     if (!cfdof) {
7646       for (b = 0; b < fdof; ++b) {
7647         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7648         const PetscInt ind    = indperm ? indperm[preind] : preind;
7649 
7650         indices[ind] = foff + b;
7651       }
7652     } else {
7653       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7654       for (b = 0; b < fdof; ++b) {
7655         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7656         const PetscInt ind    = indperm ? indperm[preind] : preind;
7657 
7658         if ((cind < cfdof) && (b == fcdofs[cind])) {
7659           indices[ind] = -(foff + b + 1);
7660           ++cind;
7661         } else {
7662           indices[ind] = foff + b - cind;
7663         }
7664       }
7665     }
7666     foffs[f] += fdof;
7667   }
7668   PetscFunctionReturn(PETSC_SUCCESS);
7669 }
7670 
7671 static PetscErrorCode DMPlexAnchorsGetSubMatIndices(PetscInt nPoints, const PetscInt pnts[], PetscSection section, PetscSection cSec, PetscInt tmpIndices[], PetscInt fieldOffsets[], PetscInt indices[], const PetscInt ***perms)
7672 {
7673   PetscInt numFields, sStart, sEnd, cStart, cEnd;
7674 
7675   PetscFunctionBegin;
7676   PetscCall(PetscSectionGetNumFields(section, &numFields));
7677   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7678   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7679   for (PetscInt p = 0; p < nPoints; p++) {
7680     PetscInt     b       = pnts[2 * p];
7681     PetscInt     bSecDof = 0, bOff;
7682     PetscInt     cSecDof = 0;
7683     PetscSection indices_section;
7684 
7685     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7686     if (!bSecDof) continue;
7687     if (b >= cStart && b < cEnd) PetscCall(PetscSectionGetDof(cSec, b, &cSecDof));
7688     indices_section = cSecDof > 0 ? cSec : section;
7689     if (numFields) {
7690       PetscInt fStart[32], fEnd[32];
7691 
7692       fStart[0] = 0;
7693       fEnd[0]   = 0;
7694       for (PetscInt f = 0; f < numFields; f++) {
7695         PetscInt fDof = 0;
7696 
7697         PetscCall(PetscSectionGetFieldDof(indices_section, b, f, &fDof));
7698         fStart[f + 1] = fStart[f] + fDof;
7699         fEnd[f + 1]   = fStart[f + 1];
7700       }
7701       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7702       // only apply permutations on one side
7703       PetscCall(DMPlexGetIndicesPointFields_Internal(indices_section, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, perms ? p : -1, NULL, tmpIndices));
7704       for (PetscInt f = 0; f < numFields; f++) {
7705         for (PetscInt i = fStart[f]; i < fEnd[f]; i++) { indices[fieldOffsets[f]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1); }
7706       }
7707     } else {
7708       PetscInt bEnd = 0;
7709 
7710       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7711       PetscCall(DMPlexGetIndicesPoint_Internal(indices_section, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, tmpIndices));
7712 
7713       for (PetscInt i = 0; i < bEnd; i++) indices[fieldOffsets[0]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1);
7714     }
7715   }
7716   PetscFunctionReturn(PETSC_SUCCESS);
7717 }
7718 
7719 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[])
7720 {
7721   Mat             cMat;
7722   PetscSection    aSec, cSec;
7723   IS              aIS;
7724   PetscInt        aStart = -1, aEnd = -1;
7725   PetscInt        sStart = -1, sEnd = -1;
7726   PetscInt        cStart = -1, cEnd = -1;
7727   const PetscInt *anchors;
7728   PetscInt        numFields, p;
7729   PetscInt        newNumPoints = 0, newNumIndices = 0;
7730   PetscInt       *newPoints, *indices, *newIndices, *tmpIndices, *tmpNewIndices;
7731   PetscInt        oldOffsets[32];
7732   PetscInt        newOffsets[32];
7733   PetscInt        oldOffsetsCopy[32];
7734   PetscInt        newOffsetsCopy[32];
7735   PetscScalar    *modMat         = NULL;
7736   PetscBool       anyConstrained = PETSC_FALSE;
7737 
7738   PetscFunctionBegin;
7739   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7740   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7741   PetscCall(PetscSectionGetNumFields(section, &numFields));
7742 
7743   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7744   /* if there are point-to-point constraints */
7745   if (aSec) {
7746     PetscCall(PetscArrayzero(newOffsets, 32));
7747     PetscCall(PetscArrayzero(oldOffsets, 32));
7748     PetscCall(ISGetIndices(aIS, &anchors));
7749     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7750     PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7751     /* figure out how many points are going to be in the new element matrix
7752      * (we allow double counting, because it's all just going to be summed
7753      * into the global matrix anyway) */
7754     for (p = 0; p < 2 * numPoints; p += 2) {
7755       PetscInt b    = points[p];
7756       PetscInt bDof = 0, bSecDof = 0;
7757 
7758       if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7759       if (!bSecDof) continue;
7760 
7761       for (PetscInt f = 0; f < numFields; f++) {
7762         PetscInt fDof = 0;
7763 
7764         PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7765         oldOffsets[f + 1] += fDof;
7766       }
7767       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7768       if (bDof) {
7769         /* this point is constrained */
7770         /* it is going to be replaced by its anchors */
7771         PetscInt bOff, q;
7772 
7773         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7774         for (q = 0; q < bDof; q++) {
7775           PetscInt a    = anchors[bOff + q];
7776           PetscInt aDof = 0;
7777 
7778           if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7779           if (aDof) {
7780             anyConstrained = PETSC_TRUE;
7781             newNumPoints += 1;
7782           }
7783           newNumIndices += aDof;
7784           for (PetscInt f = 0; f < numFields; ++f) {
7785             PetscInt fDof = 0;
7786 
7787             if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7788             newOffsets[f + 1] += fDof;
7789           }
7790         }
7791       } else {
7792         /* this point is not constrained */
7793         newNumPoints++;
7794         newNumIndices += bSecDof;
7795         for (PetscInt f = 0; f < numFields; ++f) {
7796           PetscInt fDof;
7797 
7798           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7799           newOffsets[f + 1] += fDof;
7800         }
7801       }
7802     }
7803   }
7804   if (!anyConstrained) {
7805     if (outNumPoints) *outNumPoints = 0;
7806     if (outNumIndices) *outNumIndices = 0;
7807     if (outPoints) *outPoints = NULL;
7808     if (outMat) *outMat = NULL;
7809     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7810     PetscFunctionReturn(PETSC_SUCCESS);
7811   }
7812 
7813   if (outNumPoints) *outNumPoints = newNumPoints;
7814   if (outNumIndices) *outNumIndices = newNumIndices;
7815 
7816   for (PetscInt f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7817   for (PetscInt f = 0; f < numFields; ++f) oldOffsets[f + 1] += oldOffsets[f];
7818 
7819   if (!outPoints && !outMat) {
7820     if (offsets) {
7821       for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7822     }
7823     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7824     PetscFunctionReturn(PETSC_SUCCESS);
7825   }
7826 
7827   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7828   PetscCheck(!numFields || oldOffsets[numFields] == numIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, oldOffsets[numFields], numIndices);
7829 
7830   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7831   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7832 
7833   /* output arrays */
7834   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7835   PetscCall(PetscArrayzero(newPoints, 2 * newNumPoints));
7836 
7837   // get the new Points
7838   for (PetscInt p = 0, newP = 0; p < numPoints; p++) {
7839     PetscInt b    = points[2 * p];
7840     PetscInt bDof = 0, bSecDof = 0, bOff;
7841 
7842     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7843     if (!bSecDof) continue;
7844     if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7845     if (bDof) {
7846       PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7847       for (PetscInt q = 0; q < bDof; q++) {
7848         PetscInt a = anchors[bOff + q], aDof = 0;
7849 
7850         if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7851         if (aDof) {
7852           newPoints[2 * newP]     = a;
7853           newPoints[2 * newP + 1] = 0; // orientations are accounted for in constructing the matrix, newly added points are in default orientation
7854           newP++;
7855         }
7856       }
7857     } else {
7858       newPoints[2 * newP]     = b;
7859       newPoints[2 * newP + 1] = points[2 * p + 1];
7860       newP++;
7861     }
7862   }
7863 
7864   if (outMat) {
7865     PetscScalar *tmpMat;
7866     PetscCall(PetscArraycpy(oldOffsetsCopy, oldOffsets, 32));
7867     PetscCall(PetscArraycpy(newOffsetsCopy, newOffsets, 32));
7868 
7869     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &indices));
7870     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7871     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7872     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7873 
7874     for (PetscInt i = 0; i < numIndices; i++) indices[i] = -1;
7875     for (PetscInt i = 0; i < newNumIndices; i++) newIndices[i] = -1;
7876 
7877     PetscCall(DMPlexAnchorsGetSubMatIndices(numPoints, points, section, cSec, tmpIndices, oldOffsetsCopy, indices, perms));
7878     PetscCall(DMPlexAnchorsGetSubMatIndices(newNumPoints, newPoints, section, section, tmpNewIndices, newOffsetsCopy, newIndices, NULL));
7879 
7880     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7881     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7882     PetscCall(PetscArrayzero(modMat, newNumIndices * numIndices));
7883     // for each field, insert the anchor modification into modMat
7884     for (PetscInt f = 0; f < PetscMax(1, numFields); f++) {
7885       PetscInt fStart    = oldOffsets[f];
7886       PetscInt fNewStart = newOffsets[f];
7887       for (PetscInt p = 0, newP = 0, o = fStart, oNew = fNewStart; p < numPoints; p++) {
7888         PetscInt b    = points[2 * p];
7889         PetscInt bDof = 0, bSecDof = 0, bOff;
7890 
7891         if (b >= sStart && b < sEnd) {
7892           if (numFields) {
7893             PetscCall(PetscSectionGetFieldDof(section, b, f, &bSecDof));
7894           } else {
7895             PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7896           }
7897         }
7898         if (!bSecDof) continue;
7899         if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7900         if (bDof) {
7901           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7902           for (PetscInt q = 0; q < bDof; q++, newP++) {
7903             PetscInt a = anchors[bOff + q], aDof = 0;
7904 
7905             if (a >= sStart && a < sEnd) {
7906               if (numFields) {
7907                 PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
7908               } else {
7909                 PetscCall(PetscSectionGetDof(section, a, &aDof));
7910               }
7911             }
7912             if (aDof) {
7913               PetscCall(MatGetValues(cMat, bSecDof, &indices[o], aDof, &newIndices[oNew], tmpMat));
7914               for (PetscInt d = 0; d < bSecDof; d++) {
7915                 for (PetscInt e = 0; e < aDof; e++) modMat[(o + d) * newNumIndices + oNew + e] = tmpMat[d * aDof + e];
7916               }
7917             }
7918             oNew += aDof;
7919           }
7920         } else {
7921           // Insert the identity matrix in this block
7922           for (PetscInt d = 0; d < bSecDof; d++) modMat[(o + d) * newNumIndices + oNew + d] = 1;
7923           oNew += bSecDof;
7924           newP++;
7925         }
7926         o += bSecDof;
7927       }
7928     }
7929 
7930     *outMat = modMat;
7931 
7932     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7933     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7934     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7935     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7936     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &indices));
7937   }
7938   PetscCall(ISRestoreIndices(aIS, &anchors));
7939 
7940   /* output */
7941   if (outPoints) {
7942     *outPoints = newPoints;
7943   } else {
7944     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7945   }
7946   for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7947   PetscFunctionReturn(PETSC_SUCCESS);
7948 }
7949 
7950 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)
7951 {
7952   PetscScalar *modMat        = NULL;
7953   PetscInt     newNumIndices = -1;
7954 
7955   PetscFunctionBegin;
7956   /* 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.
7957      modMat is that matrix C */
7958   PetscCall(DMPlexAnchorsGetSubMatModification(dm, section, numPoints, numIndices, points, perms, outNumPoints, &newNumIndices, outPoints, offsets, outValues ? &modMat : NULL));
7959   if (outNumIndices) *outNumIndices = newNumIndices;
7960   if (modMat) {
7961     const PetscScalar *newValues = values;
7962 
7963     if (multiplyRight) {
7964       PetscScalar *newNewValues = NULL;
7965       PetscBLASInt M, N, K;
7966       PetscScalar  a = 1.0, b = 0.0;
7967 
7968       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);
7969 
7970       PetscCall(PetscBLASIntCast(newNumIndices, &M));
7971       PetscCall(PetscBLASIntCast(numRows, &N));
7972       PetscCall(PetscBLASIntCast(numIndices, &K));
7973       PetscCall(DMGetWorkArray(dm, numRows * newNumIndices, MPIU_SCALAR, &newNewValues));
7974       // row-major to column-major conversion, right multiplication becomes left multiplication
7975       PetscCallBLAS("BLASgemm", BLASgemm_("N", "N", &M, &N, &K, &a, modMat, &M, newValues, &K, &b, newNewValues, &M));
7976       numCols   = newNumIndices;
7977       newValues = newNewValues;
7978     }
7979 
7980     if (multiplyLeft) {
7981       PetscScalar *newNewValues = NULL;
7982       PetscBLASInt M, N, K;
7983       PetscScalar  a = 1.0, b = 0.0;
7984 
7985       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);
7986 
7987       PetscCall(PetscBLASIntCast(numCols, &M));
7988       PetscCall(PetscBLASIntCast(newNumIndices, &N));
7989       PetscCall(PetscBLASIntCast(numIndices, &K));
7990       PetscCall(DMGetWorkArray(dm, newNumIndices * numCols, MPIU_SCALAR, &newNewValues));
7991       // row-major to column-major conversion, left multiplication becomes right multiplication
7992       PetscCallBLAS("BLASgemm", BLASgemm_("N", "T", &M, &N, &K, &a, newValues, &M, modMat, &N, &b, newNewValues, &M));
7993       if (newValues != values) PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &newValues));
7994       newValues = newNewValues;
7995     }
7996     *outValues = (PetscScalar *)newValues;
7997     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7998   }
7999   PetscFunctionReturn(PETSC_SUCCESS);
8000 }
8001 
8002 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)
8003 {
8004   PetscFunctionBegin;
8005   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, numPoints, numIndices, points, perms, numIndices, numIndices, values, outNumPoints, outNumIndices, outPoints, outValues, offsets, PETSC_TRUE, multiplyLeft));
8006   PetscFunctionReturn(PETSC_SUCCESS);
8007 }
8008 
8009 static PetscErrorCode DMPlexGetClosureIndicesSize_Internal(DM dm, PetscSection section, PetscInt point, PetscInt *closureSize)
8010 {
8011   /* Closure ordering */
8012   PetscSection    clSection;
8013   IS              clPoints;
8014   const PetscInt *clp;
8015   PetscInt       *points;
8016   PetscInt        Ncl, Ni = 0;
8017 
8018   PetscFunctionBeginHot;
8019   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
8020   for (PetscInt p = 0; p < Ncl * 2; p += 2) {
8021     PetscInt dof;
8022 
8023     PetscCall(PetscSectionGetDof(section, points[p], &dof));
8024     Ni += dof;
8025   }
8026   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8027   *closureSize = Ni;
8028   PetscFunctionReturn(PETSC_SUCCESS);
8029 }
8030 
8031 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)
8032 {
8033   /* Closure ordering */
8034   PetscSection    clSection;
8035   IS              clPoints;
8036   const PetscInt *clp;
8037   PetscInt       *points;
8038   const PetscInt *clperm = NULL;
8039   /* Dof permutation and sign flips */
8040   const PetscInt    **perms[32] = {NULL};
8041   const PetscScalar **flips[32] = {NULL};
8042   PetscScalar        *valCopy   = NULL;
8043   /* Hanging node constraints */
8044   PetscInt    *pointsC = NULL;
8045   PetscScalar *valuesC = NULL;
8046   PetscInt     NclC, NiC;
8047 
8048   PetscInt *idx;
8049   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
8050   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
8051   PetscInt  idxStart, idxEnd;
8052   PetscInt  nRows, nCols;
8053 
8054   PetscFunctionBeginHot;
8055   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8056   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8057   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
8058   PetscAssertPointer(numRows, 6);
8059   PetscAssertPointer(numCols, 7);
8060   if (indices) PetscAssertPointer(indices, 8);
8061   if (outOffsets) PetscAssertPointer(outOffsets, 9);
8062   if (values) PetscAssertPointer(values, 10);
8063   PetscCall(PetscSectionGetNumFields(section, &Nf));
8064   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
8065   PetscCall(PetscArrayzero(offsets, 32));
8066   /* 1) Get points in closure */
8067   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
8068   if (useClPerm) {
8069     PetscInt depth, clsize;
8070     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
8071     for (clsize = 0, p = 0; p < Ncl; p++) {
8072       PetscInt dof;
8073       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
8074       clsize += dof;
8075     }
8076     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
8077   }
8078   /* 2) Get number of indices on these points and field offsets from section */
8079   for (p = 0; p < Ncl * 2; p += 2) {
8080     PetscInt dof, fdof;
8081 
8082     PetscCall(PetscSectionGetDof(section, points[p], &dof));
8083     for (f = 0; f < Nf; ++f) {
8084       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
8085       offsets[f + 1] += fdof;
8086     }
8087     Ni += dof;
8088   }
8089   if (*numRows == -1) *numRows = Ni;
8090   if (*numCols == -1) *numCols = Ni;
8091   nRows = *numRows;
8092   nCols = *numCols;
8093   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
8094   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
8095   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
8096   if (multiplyRight) PetscCheck(nCols == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " columns, got %" PetscInt_FMT, Ni, nCols);
8097   if (multiplyLeft) PetscCheck(nRows == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " rows, got %" PetscInt_FMT, Ni, nRows);
8098   for (f = 0; f < PetscMax(1, Nf); ++f) {
8099     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8100     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
8101     /* may need to apply sign changes to the element matrix */
8102     if (values && flips[f]) {
8103       PetscInt foffset = offsets[f];
8104 
8105       for (p = 0; p < Ncl; ++p) {
8106         PetscInt           pnt  = points[2 * p], fdof;
8107         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
8108 
8109         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
8110         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
8111         if (flip) {
8112           PetscInt i, j, k;
8113 
8114           if (!valCopy) {
8115             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8116             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
8117             *values = valCopy;
8118           }
8119           for (i = 0; i < fdof; ++i) {
8120             PetscScalar fval = flip[i];
8121 
8122             if (multiplyRight) {
8123               for (k = 0; k < nRows; ++k) { valCopy[Ni * k + (foffset + i)] *= fval; }
8124             }
8125             if (multiplyLeft) {
8126               for (k = 0; k < nCols; ++k) { valCopy[nCols * (foffset + i) + k] *= fval; }
8127             }
8128           }
8129         }
8130         foffset += fdof;
8131       }
8132     }
8133   }
8134   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
8135   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, Ncl, Ni, points, perms, nRows, nCols, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, multiplyRight, multiplyLeft));
8136   if (NclC) {
8137     if (multiplyRight) *numCols = NiC;
8138     if (multiplyLeft) *numRows = NiC;
8139     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8140     for (f = 0; f < PetscMax(1, Nf); ++f) {
8141       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8142       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8143     }
8144     for (f = 0; f < PetscMax(1, Nf); ++f) {
8145       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
8146       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
8147     }
8148     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8149     Ncl    = NclC;
8150     Ni     = NiC;
8151     points = pointsC;
8152     if (values) *values = valuesC;
8153   }
8154   /* 5) Calculate indices */
8155   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
8156   PetscCall(PetscSectionGetChart(idxSection, &idxStart, &idxEnd));
8157   if (Nf) {
8158     PetscInt  idxOff;
8159     PetscBool useFieldOffsets;
8160 
8161     if (outOffsets) {
8162       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
8163     }
8164     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
8165     if (useFieldOffsets) {
8166       for (p = 0; p < Ncl; ++p) {
8167         const PetscInt pnt = points[p * 2];
8168 
8169         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
8170       }
8171     } else {
8172       for (p = 0; p < Ncl; ++p) {
8173         const PetscInt pnt = points[p * 2];
8174 
8175         if (pnt < idxStart || pnt >= idxEnd) continue;
8176         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8177         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8178          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
8179          * global section. */
8180         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
8181       }
8182     }
8183   } else {
8184     PetscInt off = 0, idxOff;
8185 
8186     for (p = 0; p < Ncl; ++p) {
8187       const PetscInt  pnt  = points[p * 2];
8188       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
8189 
8190       if (pnt < idxStart || pnt >= idxEnd) continue;
8191       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8192       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8193        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
8194       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
8195     }
8196   }
8197   /* 6) Cleanup */
8198   for (f = 0; f < PetscMax(1, Nf); ++f) {
8199     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8200     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8201   }
8202   if (NclC) {
8203     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
8204   } else {
8205     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8206   }
8207 
8208   if (indices) *indices = idx;
8209   PetscFunctionReturn(PETSC_SUCCESS);
8210 }
8211 
8212 /*@C
8213   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
8214 
8215   Not collective
8216 
8217   Input Parameters:
8218 + dm         - The `DM`
8219 . section    - The `PetscSection` describing the points (a local section)
8220 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8221 . point      - The point defining the closure
8222 - useClPerm  - Use the closure point permutation if available
8223 
8224   Output Parameters:
8225 + numIndices - The number of dof indices in the closure of point with the input sections
8226 . indices    - The dof indices
8227 . outOffsets - Array to write the field offsets into, or `NULL`
8228 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8229 
8230   Level: advanced
8231 
8232   Notes:
8233   Call `DMPlexRestoreClosureIndices()` to free allocated memory
8234 
8235   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8236   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
8237   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8238   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
8239   indices (with the above semantics) are implied.
8240 
8241 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
8242           `PetscSection`, `DMGetGlobalSection()`
8243 @*/
8244 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PeOp PetscInt outOffsets[], PeOp PetscScalar *values[])
8245 {
8246   PetscInt numRows = -1, numCols = -1;
8247 
8248   PetscFunctionBeginHot;
8249   PetscCall(DMPlexGetClosureIndices_Internal(dm, section, idxSection, point, useClPerm, &numRows, &numCols, indices, outOffsets, values, PETSC_TRUE, PETSC_TRUE));
8250   PetscCheck(numRows == numCols, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Symmetric matrix transformation produces rectangular dimensions (%" PetscInt_FMT ", %" PetscInt_FMT ")", numRows, numCols);
8251   *numIndices = numRows;
8252   PetscFunctionReturn(PETSC_SUCCESS);
8253 }
8254 
8255 /*@C
8256   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
8257 
8258   Not collective
8259 
8260   Input Parameters:
8261 + dm         - The `DM`
8262 . section    - The `PetscSection` describing the points (a local section)
8263 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8264 . point      - The point defining the closure
8265 - useClPerm  - Use the closure point permutation if available
8266 
8267   Output Parameters:
8268 + numIndices - The number of dof indices in the closure of point with the input sections
8269 . indices    - The dof indices
8270 . outOffsets - Array to write the field offsets into, or `NULL`
8271 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8272 
8273   Level: advanced
8274 
8275   Notes:
8276   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
8277 
8278   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8279   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
8280   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8281   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
8282   indices (with the above semantics) are implied.
8283 
8284 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
8285 @*/
8286 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PeOp PetscInt outOffsets[], PeOp PetscScalar *values[])
8287 {
8288   PetscFunctionBegin;
8289   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8290   PetscAssertPointer(indices, 7);
8291   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
8292   PetscFunctionReturn(PETSC_SUCCESS);
8293 }
8294 
8295 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8296 {
8297   DM_Plex           *mesh = (DM_Plex *)dm->data;
8298   PetscInt          *indices;
8299   PetscInt           numIndices;
8300   const PetscScalar *valuesOrig = values;
8301   PetscErrorCode     ierr;
8302 
8303   PetscFunctionBegin;
8304   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8305   if (!section) PetscCall(DMGetLocalSection(dm, &section));
8306   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8307   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
8308   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
8309   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
8310 
8311   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
8312 
8313   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
8314   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8315   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
8316   if (ierr) {
8317     PetscMPIInt rank;
8318 
8319     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8320     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8321     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
8322     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8323     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8324     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
8325   }
8326   if (mesh->printFEM > 1) {
8327     PetscInt i;
8328     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
8329     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
8330     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8331   }
8332 
8333   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8334   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8335   PetscFunctionReturn(PETSC_SUCCESS);
8336 }
8337 
8338 /*@C
8339   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
8340 
8341   Not collective
8342 
8343   Input Parameters:
8344 + dm            - The `DM`
8345 . section       - The section describing the layout in `v`, or `NULL` to use the default section
8346 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
8347 . A             - The matrix
8348 . point         - The point in the `DM`
8349 . values        - The array of values
8350 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8351 
8352   Level: intermediate
8353 
8354 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8355 @*/
8356 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8357 {
8358   PetscFunctionBegin;
8359   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
8360   PetscFunctionReturn(PETSC_SUCCESS);
8361 }
8362 
8363 /*@C
8364   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
8365 
8366   Not collective
8367 
8368   Input Parameters:
8369 + dmRow            - The `DM` for the row fields
8370 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
8371 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
8372 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
8373 . dmCol            - The `DM` for the column fields
8374 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
8375 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
8376 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
8377 . A                - The matrix
8378 . point            - The point in the `DM`
8379 . values           - The array of values
8380 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8381 
8382   Level: intermediate
8383 
8384 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8385 @*/
8386 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)
8387 {
8388   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
8389   PetscInt          *indicesRow, *indicesCol;
8390   PetscInt           numIndicesRow = -1, numIndicesCol = -1;
8391   const PetscScalar *valuesV0 = values, *valuesV1, *valuesV2;
8392 
8393   PetscErrorCode ierr;
8394 
8395   PetscFunctionBegin;
8396   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
8397   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
8398   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
8399   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
8400   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
8401   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
8402   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
8403   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
8404   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
8405   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
8406   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
8407 
8408   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmRow, sectionRow, point, &numIndicesRow));
8409   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmCol, sectionCol, point, &numIndicesCol));
8410   valuesV1 = valuesV0;
8411   PetscCall(DMPlexGetClosureIndices_Internal(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV1, PETSC_FALSE, PETSC_TRUE));
8412   valuesV2 = valuesV1;
8413   PetscCall(DMPlexGetClosureIndices_Internal(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesRow, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2, PETSC_TRUE, PETSC_FALSE));
8414 
8415   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2));
8416   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8417   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2, mode);
8418   if (ierr) {
8419     PetscMPIInt rank;
8420 
8421     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8422     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8423     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8424     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV2));
8425     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8426     if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8427     if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8428   }
8429 
8430   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2));
8431   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8432   if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8433   if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8434   PetscFunctionReturn(PETSC_SUCCESS);
8435 }
8436 
8437 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8438 {
8439   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8440   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8441   PetscInt       *cpoints = NULL;
8442   PetscInt       *findices, *cindices;
8443   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8444   PetscInt        foffsets[32], coffsets[32];
8445   DMPolytopeType  ct;
8446   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8447   PetscErrorCode  ierr;
8448 
8449   PetscFunctionBegin;
8450   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8451   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8452   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8453   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8454   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8455   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8456   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8457   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8458   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8459   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8460   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8461   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8462   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8463   PetscCall(PetscArrayzero(foffsets, 32));
8464   PetscCall(PetscArrayzero(coffsets, 32));
8465   /* Column indices */
8466   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8467   maxFPoints = numCPoints;
8468   /* Compress out points not in the section */
8469   /*   TODO: Squeeze out points with 0 dof as well */
8470   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8471   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8472     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8473       cpoints[q * 2]     = cpoints[p];
8474       cpoints[q * 2 + 1] = cpoints[p + 1];
8475       ++q;
8476     }
8477   }
8478   numCPoints = q;
8479   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8480     PetscInt fdof;
8481 
8482     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8483     if (!dof) continue;
8484     for (f = 0; f < numFields; ++f) {
8485       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8486       coffsets[f + 1] += fdof;
8487     }
8488     numCIndices += dof;
8489   }
8490   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8491   /* Row indices */
8492   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8493   {
8494     DMPlexTransform tr;
8495     DMPolytopeType *rct;
8496     PetscInt       *rsize, *rcone, *rornt, Nt;
8497 
8498     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8499     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8500     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8501     numSubcells = rsize[Nt - 1];
8502     PetscCall(DMPlexTransformDestroy(&tr));
8503   }
8504   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8505   for (r = 0, q = 0; r < numSubcells; ++r) {
8506     /* TODO Map from coarse to fine cells */
8507     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8508     /* Compress out points not in the section */
8509     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8510     for (p = 0; p < numFPoints * 2; p += 2) {
8511       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8512         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8513         if (!dof) continue;
8514         for (s = 0; s < q; ++s)
8515           if (fpoints[p] == ftotpoints[s * 2]) break;
8516         if (s < q) continue;
8517         ftotpoints[q * 2]     = fpoints[p];
8518         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8519         ++q;
8520       }
8521     }
8522     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8523   }
8524   numFPoints = q;
8525   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8526     PetscInt fdof;
8527 
8528     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8529     if (!dof) continue;
8530     for (f = 0; f < numFields; ++f) {
8531       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8532       foffsets[f + 1] += fdof;
8533     }
8534     numFIndices += dof;
8535   }
8536   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8537 
8538   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8539   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8540   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8541   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8542   if (numFields) {
8543     const PetscInt **permsF[32] = {NULL};
8544     const PetscInt **permsC[32] = {NULL};
8545 
8546     for (f = 0; f < numFields; f++) {
8547       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8548       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8549     }
8550     for (p = 0; p < numFPoints; p++) {
8551       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8552       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8553     }
8554     for (p = 0; p < numCPoints; p++) {
8555       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8556       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8557     }
8558     for (f = 0; f < numFields; f++) {
8559       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8560       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8561     }
8562   } else {
8563     const PetscInt **permsF = NULL;
8564     const PetscInt **permsC = NULL;
8565 
8566     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8567     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8568     for (p = 0, off = 0; p < numFPoints; p++) {
8569       const PetscInt *perm = permsF ? permsF[p] : NULL;
8570 
8571       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8572       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8573     }
8574     for (p = 0, off = 0; p < numCPoints; p++) {
8575       const PetscInt *perm = permsC ? permsC[p] : NULL;
8576 
8577       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8578       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8579     }
8580     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8581     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8582   }
8583   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8584   /* TODO: flips */
8585   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8586   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8587   if (ierr) {
8588     PetscMPIInt rank;
8589 
8590     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8591     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8592     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8593     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8594     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8595   }
8596   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8597   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8598   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8599   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8600   PetscFunctionReturn(PETSC_SUCCESS);
8601 }
8602 
8603 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8604 {
8605   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8606   PetscInt       *cpoints      = NULL;
8607   PetscInt        foffsets[32] = {0}, coffsets[32] = {0};
8608   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8609   DMPolytopeType  ct;
8610   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8611 
8612   PetscFunctionBegin;
8613   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8614   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8615   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8616   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8617   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8618   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8619   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8620   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8621   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8622   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8623   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8624   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8625   /* Column indices */
8626   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8627   maxFPoints = numCPoints;
8628   /* Compress out points not in the section */
8629   /*   TODO: Squeeze out points with 0 dof as well */
8630   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8631   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8632     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8633       cpoints[q * 2]     = cpoints[p];
8634       cpoints[q * 2 + 1] = cpoints[p + 1];
8635       ++q;
8636     }
8637   }
8638   numCPoints = q;
8639   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8640     PetscInt fdof;
8641 
8642     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8643     if (!dof) continue;
8644     for (f = 0; f < numFields; ++f) {
8645       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8646       coffsets[f + 1] += fdof;
8647     }
8648     numCIndices += dof;
8649   }
8650   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8651   /* Row indices */
8652   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8653   {
8654     DMPlexTransform tr;
8655     DMPolytopeType *rct;
8656     PetscInt       *rsize, *rcone, *rornt, Nt;
8657 
8658     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8659     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8660     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8661     numSubcells = rsize[Nt - 1];
8662     PetscCall(DMPlexTransformDestroy(&tr));
8663   }
8664   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8665   for (r = 0, q = 0; r < numSubcells; ++r) {
8666     /* TODO Map from coarse to fine cells */
8667     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8668     /* Compress out points not in the section */
8669     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8670     for (p = 0; p < numFPoints * 2; p += 2) {
8671       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8672         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8673         if (!dof) continue;
8674         for (s = 0; s < q; ++s)
8675           if (fpoints[p] == ftotpoints[s * 2]) break;
8676         if (s < q) continue;
8677         ftotpoints[q * 2]     = fpoints[p];
8678         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8679         ++q;
8680       }
8681     }
8682     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8683   }
8684   numFPoints = q;
8685   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8686     PetscInt fdof;
8687 
8688     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8689     if (!dof) continue;
8690     for (f = 0; f < numFields; ++f) {
8691       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8692       foffsets[f + 1] += fdof;
8693     }
8694     numFIndices += dof;
8695   }
8696   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8697 
8698   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8699   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8700   if (numFields) {
8701     const PetscInt **permsF[32] = {NULL};
8702     const PetscInt **permsC[32] = {NULL};
8703 
8704     for (f = 0; f < numFields; f++) {
8705       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8706       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8707     }
8708     for (p = 0; p < numFPoints; p++) {
8709       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8710       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8711     }
8712     for (p = 0; p < numCPoints; p++) {
8713       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8714       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8715     }
8716     for (f = 0; f < numFields; f++) {
8717       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8718       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8719     }
8720   } else {
8721     const PetscInt **permsF = NULL;
8722     const PetscInt **permsC = NULL;
8723 
8724     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8725     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8726     for (p = 0, off = 0; p < numFPoints; p++) {
8727       const PetscInt *perm = permsF ? permsF[p] : NULL;
8728 
8729       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8730       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8731     }
8732     for (p = 0, off = 0; p < numCPoints; p++) {
8733       const PetscInt *perm = permsC ? permsC[p] : NULL;
8734 
8735       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8736       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8737     }
8738     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8739     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8740   }
8741   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8742   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8743   PetscFunctionReturn(PETSC_SUCCESS);
8744 }
8745 
8746 /*@
8747   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8748 
8749   Input Parameter:
8750 . dm - The `DMPLEX` object
8751 
8752   Output Parameter:
8753 . cellHeight - The height of a cell
8754 
8755   Level: developer
8756 
8757 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8758 @*/
8759 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8760 {
8761   DM_Plex *mesh = (DM_Plex *)dm->data;
8762 
8763   PetscFunctionBegin;
8764   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8765   PetscAssertPointer(cellHeight, 2);
8766   *cellHeight = mesh->vtkCellHeight;
8767   PetscFunctionReturn(PETSC_SUCCESS);
8768 }
8769 
8770 /*@
8771   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8772 
8773   Input Parameters:
8774 + dm         - The `DMPLEX` object
8775 - cellHeight - The height of a cell
8776 
8777   Level: developer
8778 
8779 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8780 @*/
8781 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8782 {
8783   DM_Plex *mesh = (DM_Plex *)dm->data;
8784 
8785   PetscFunctionBegin;
8786   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8787   mesh->vtkCellHeight = cellHeight;
8788   PetscFunctionReturn(PETSC_SUCCESS);
8789 }
8790 
8791 /*@
8792   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8793 
8794   Input Parameters:
8795 + dm - The `DMPLEX` object
8796 - ct - The `DMPolytopeType` of the cell
8797 
8798   Output Parameters:
8799 + start - The first cell of this type, or `NULL`
8800 - end   - The upper bound on this celltype, or `NULL`
8801 
8802   Level: advanced
8803 
8804 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8805 @*/
8806 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PeOp PetscInt *start, PeOp PetscInt *end)
8807 {
8808   DM_Plex *mesh = (DM_Plex *)dm->data;
8809   DMLabel  label;
8810   PetscInt pStart, pEnd;
8811 
8812   PetscFunctionBegin;
8813   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8814   if (start) {
8815     PetscAssertPointer(start, 3);
8816     *start = 0;
8817   }
8818   if (end) {
8819     PetscAssertPointer(end, 4);
8820     *end = 0;
8821   }
8822   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8823   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8824   if (mesh->tr) {
8825     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8826   } else {
8827     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8828     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8829     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8830   }
8831   PetscFunctionReturn(PETSC_SUCCESS);
8832 }
8833 
8834 /*@
8835   DMPlexGetDepthStratumGlobalSize - Get the global size for a given depth stratum
8836 
8837   Input Parameters:
8838 + dm    - The `DMPLEX` object
8839 - depth - The depth for the given point stratum
8840 
8841   Output Parameter:
8842 . gsize - The global number of points in the stratum
8843 
8844   Level: advanced
8845 
8846 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8847 @*/
8848 PetscErrorCode DMPlexGetDepthStratumGlobalSize(DM dm, PetscInt depth, PetscInt *gsize)
8849 {
8850   PetscSF         sf;
8851   const PetscInt *leaves;
8852   PetscInt        Nl, loc, start, end, lsize = 0;
8853 
8854   PetscFunctionBegin;
8855   PetscCall(DMGetPointSF(dm, &sf));
8856   PetscCall(PetscSFGetGraph(sf, NULL, &Nl, &leaves, NULL));
8857   PetscCall(DMPlexGetDepthStratum(dm, depth, &start, &end));
8858   for (PetscInt p = start; p < end; ++p) {
8859     PetscCall(PetscFindInt(p, Nl, leaves, &loc));
8860     if (loc < 0) ++lsize;
8861   }
8862   PetscCallMPI(MPI_Allreduce(&lsize, gsize, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
8863   PetscFunctionReturn(PETSC_SUCCESS);
8864 }
8865 
8866 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8867 {
8868   PetscSection section, globalSection;
8869   PetscInt    *numbers, p;
8870 
8871   PetscFunctionBegin;
8872   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8873   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8874   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8875   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8876   PetscCall(PetscSectionSetUp(section));
8877   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &globalSection));
8878   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8879   for (p = pStart; p < pEnd; ++p) {
8880     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8881     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8882     else numbers[p - pStart] += shift;
8883   }
8884   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8885   if (globalSize) {
8886     PetscLayout layout;
8887     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8888     PetscCall(PetscLayoutGetSize(layout, globalSize));
8889     PetscCall(PetscLayoutDestroy(&layout));
8890   }
8891   PetscCall(PetscSectionDestroy(&section));
8892   PetscCall(PetscSectionDestroy(&globalSection));
8893   PetscFunctionReturn(PETSC_SUCCESS);
8894 }
8895 
8896 /*@
8897   DMPlexCreateCellNumbering - Get a global cell numbering for all cells on this process
8898 
8899   Input Parameters:
8900 + dm         - The `DMPLEX` object
8901 - includeAll - Whether to include all cells, or just the simplex and box cells
8902 
8903   Output Parameter:
8904 . globalCellNumbers - Global cell numbers for all cells on this process
8905 
8906   Level: developer
8907 
8908 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`
8909 @*/
8910 PetscErrorCode DMPlexCreateCellNumbering(DM dm, PetscBool includeAll, IS *globalCellNumbers)
8911 {
8912   PetscInt cellHeight, cStart, cEnd;
8913 
8914   PetscFunctionBegin;
8915   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8916   if (includeAll) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8917   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8918   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8919   PetscFunctionReturn(PETSC_SUCCESS);
8920 }
8921 
8922 /*@
8923   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8924 
8925   Input Parameter:
8926 . dm - The `DMPLEX` object
8927 
8928   Output Parameter:
8929 . globalCellNumbers - Global cell numbers for all cells on this process
8930 
8931   Level: developer
8932 
8933 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateCellNumbering()`, `DMPlexGetVertexNumbering()`
8934 @*/
8935 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8936 {
8937   DM_Plex *mesh = (DM_Plex *)dm->data;
8938 
8939   PetscFunctionBegin;
8940   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8941   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8942   *globalCellNumbers = mesh->globalCellNumbers;
8943   PetscFunctionReturn(PETSC_SUCCESS);
8944 }
8945 
8946 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8947 {
8948   PetscInt vStart, vEnd;
8949 
8950   PetscFunctionBegin;
8951   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8952   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8953   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8954   PetscFunctionReturn(PETSC_SUCCESS);
8955 }
8956 
8957 /*@
8958   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8959 
8960   Input Parameter:
8961 . dm - The `DMPLEX` object
8962 
8963   Output Parameter:
8964 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8965 
8966   Level: developer
8967 
8968 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8969 @*/
8970 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8971 {
8972   DM_Plex *mesh = (DM_Plex *)dm->data;
8973 
8974   PetscFunctionBegin;
8975   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8976   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8977   *globalVertexNumbers = mesh->globalVertexNumbers;
8978   PetscFunctionReturn(PETSC_SUCCESS);
8979 }
8980 
8981 /*@
8982   DMPlexCreatePointNumbering - Create a global numbering for all points.
8983 
8984   Collective
8985 
8986   Input Parameter:
8987 . dm - The `DMPLEX` object
8988 
8989   Output Parameter:
8990 . globalPointNumbers - Global numbers for all points on this process
8991 
8992   Level: developer
8993 
8994   Notes:
8995   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8996   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8997   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8998   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8999 
9000   The partitioned mesh is
9001   ```
9002   (2)--0--(3)--1--(4)    (1)--0--(2)
9003   ```
9004   and its global numbering is
9005   ```
9006   (3)--0--(4)--1--(5)--2--(6)
9007   ```
9008   Then the global numbering is provided as
9009   ```
9010   [0] Number of indices in set 5
9011   [0] 0 0
9012   [0] 1 1
9013   [0] 2 3
9014   [0] 3 4
9015   [0] 4 -6
9016   [1] Number of indices in set 3
9017   [1] 0 2
9018   [1] 1 5
9019   [1] 2 6
9020   ```
9021 
9022 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
9023 @*/
9024 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
9025 {
9026   IS        nums[4];
9027   PetscInt  depths[4], gdepths[4], starts[4];
9028   PetscInt  depth, d, shift = 0;
9029   PetscBool empty = PETSC_FALSE;
9030 
9031   PetscFunctionBegin;
9032   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9033   PetscCall(DMPlexGetDepth(dm, &depth));
9034   // For unstratified meshes use dim instead of depth
9035   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
9036   // If any stratum is empty, we must mark all empty
9037   for (d = 0; d <= depth; ++d) {
9038     PetscInt end;
9039 
9040     depths[d] = depth - d;
9041     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
9042     if (!(starts[d] - end)) empty = PETSC_TRUE;
9043   }
9044   if (empty)
9045     for (d = 0; d <= depth; ++d) {
9046       depths[d] = -1;
9047       starts[d] = -1;
9048     }
9049   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
9050   PetscCallMPI(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
9051   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]);
9052   // Note here that 'shift' is collective, so that the numbering is stratified by depth
9053   for (d = 0; d <= depth; ++d) {
9054     PetscInt pStart, pEnd, gsize;
9055 
9056     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
9057     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
9058     shift += gsize;
9059   }
9060   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
9061   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
9062   PetscFunctionReturn(PETSC_SUCCESS);
9063 }
9064 
9065 /*@
9066   DMPlexCreateEdgeNumbering - Create a global numbering for edges.
9067 
9068   Collective
9069 
9070   Input Parameter:
9071 . dm - The `DMPLEX` object
9072 
9073   Output Parameter:
9074 . globalEdgeNumbers - Global numbers for all edges on this process
9075 
9076   Level: developer
9077 
9078   Notes:
9079   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).
9080 
9081 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`, `DMPlexCreatePointNumbering()`
9082 @*/
9083 PetscErrorCode DMPlexCreateEdgeNumbering(DM dm, IS *globalEdgeNumbers)
9084 {
9085   PetscSF  sf;
9086   PetscInt eStart, eEnd;
9087 
9088   PetscFunctionBegin;
9089   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9090   PetscCall(DMGetPointSF(dm, &sf));
9091   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9092   PetscCall(DMPlexCreateNumbering_Plex(dm, eStart, eEnd, 0, NULL, sf, globalEdgeNumbers));
9093   PetscFunctionReturn(PETSC_SUCCESS);
9094 }
9095 
9096 /*@
9097   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
9098 
9099   Input Parameter:
9100 . dm - The `DMPLEX` object
9101 
9102   Output Parameter:
9103 . ranks - The rank field
9104 
9105   Options Database Key:
9106 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
9107 
9108   Level: intermediate
9109 
9110 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
9111 @*/
9112 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
9113 {
9114   DM             rdm;
9115   PetscFE        fe;
9116   PetscScalar   *r;
9117   PetscMPIInt    rank;
9118   DMPolytopeType ct;
9119   PetscInt       dim, cStart, cEnd, c;
9120   PetscBool      simplex;
9121 
9122   PetscFunctionBeginUser;
9123   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9124   PetscAssertPointer(ranks, 2);
9125   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
9126   PetscCall(DMClone(dm, &rdm));
9127   PetscCall(DMGetDimension(rdm, &dim));
9128   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
9129   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
9130   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
9131   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
9132   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
9133   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9134   PetscCall(PetscFEDestroy(&fe));
9135   PetscCall(DMCreateDS(rdm));
9136   PetscCall(DMCreateGlobalVector(rdm, ranks));
9137   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
9138   PetscCall(VecGetArray(*ranks, &r));
9139   for (c = cStart; c < cEnd; ++c) {
9140     PetscScalar *lr;
9141 
9142     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
9143     if (lr) *lr = rank;
9144   }
9145   PetscCall(VecRestoreArray(*ranks, &r));
9146   PetscCall(DMDestroy(&rdm));
9147   PetscFunctionReturn(PETSC_SUCCESS);
9148 }
9149 
9150 /*@
9151   DMPlexCreateLabelField - Create a field whose value is the label value for that point
9152 
9153   Input Parameters:
9154 + dm    - The `DMPLEX`
9155 - label - The `DMLabel`
9156 
9157   Output Parameter:
9158 . val - The label value field
9159 
9160   Options Database Key:
9161 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
9162 
9163   Level: intermediate
9164 
9165 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
9166 @*/
9167 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
9168 {
9169   DM             rdm, plex;
9170   Vec            lval;
9171   PetscSection   section;
9172   PetscFE        fe;
9173   PetscScalar   *v;
9174   PetscInt       dim, pStart, pEnd, p, cStart;
9175   DMPolytopeType ct;
9176   char           name[PETSC_MAX_PATH_LEN];
9177   const char    *lname, *prefix;
9178 
9179   PetscFunctionBeginUser;
9180   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9181   PetscAssertPointer(label, 2);
9182   PetscAssertPointer(val, 3);
9183   PetscCall(DMClone(dm, &rdm));
9184   PetscCall(DMConvert(rdm, DMPLEX, &plex));
9185   PetscCall(DMPlexGetHeightStratum(plex, 0, &cStart, NULL));
9186   PetscCall(DMPlexGetCellType(plex, cStart, &ct));
9187   PetscCall(DMDestroy(&plex));
9188   PetscCall(DMGetDimension(rdm, &dim));
9189   PetscCall(DMGetOptionsPrefix(dm, &prefix));
9190   PetscCall(PetscObjectGetName((PetscObject)label, &lname));
9191   PetscCall(PetscSNPrintf(name, sizeof(name), "%s%s_", prefix ? prefix : "", lname));
9192   PetscCall(PetscFECreateByCell(PETSC_COMM_SELF, dim, 1, ct, name, -1, &fe));
9193   PetscCall(PetscObjectSetName((PetscObject)fe, ""));
9194   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9195   PetscCall(PetscFEDestroy(&fe));
9196   PetscCall(DMCreateDS(rdm));
9197   PetscCall(DMCreateGlobalVector(rdm, val));
9198   PetscCall(DMCreateLocalVector(rdm, &lval));
9199   PetscCall(PetscObjectSetName((PetscObject)*val, lname));
9200   PetscCall(DMGetLocalSection(rdm, &section));
9201   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
9202   PetscCall(VecGetArray(lval, &v));
9203   for (p = pStart; p < pEnd; ++p) {
9204     PetscInt cval, dof, off;
9205 
9206     PetscCall(PetscSectionGetDof(section, p, &dof));
9207     if (!dof) continue;
9208     PetscCall(DMLabelGetValue(label, p, &cval));
9209     PetscCall(PetscSectionGetOffset(section, p, &off));
9210     for (PetscInt d = 0; d < dof; d++) v[off + d] = cval;
9211   }
9212   PetscCall(VecRestoreArray(lval, &v));
9213   PetscCall(DMLocalToGlobal(rdm, lval, INSERT_VALUES, *val));
9214   PetscCall(VecDestroy(&lval));
9215   PetscCall(DMDestroy(&rdm));
9216   PetscFunctionReturn(PETSC_SUCCESS);
9217 }
9218 
9219 /*@
9220   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
9221 
9222   Input Parameter:
9223 . dm - The `DMPLEX` object
9224 
9225   Level: developer
9226 
9227   Notes:
9228   This is a useful diagnostic when creating meshes programmatically.
9229 
9230   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9231 
9232 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9233 @*/
9234 PetscErrorCode DMPlexCheckSymmetry(DM dm)
9235 {
9236   PetscSection    coneSection, supportSection;
9237   const PetscInt *cone, *support;
9238   PetscInt        coneSize, c, supportSize, s;
9239   PetscInt        pStart, pEnd, p, pp, csize, ssize;
9240   PetscBool       storagecheck = PETSC_TRUE;
9241 
9242   PetscFunctionBegin;
9243   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9244   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
9245   PetscCall(DMPlexGetConeSection(dm, &coneSection));
9246   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
9247   /* Check that point p is found in the support of its cone points, and vice versa */
9248   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9249   for (p = pStart; p < pEnd; ++p) {
9250     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
9251     PetscCall(DMPlexGetCone(dm, p, &cone));
9252     for (c = 0; c < coneSize; ++c) {
9253       PetscBool dup = PETSC_FALSE;
9254       PetscInt  d;
9255       for (d = c - 1; d >= 0; --d) {
9256         if (cone[c] == cone[d]) {
9257           dup = PETSC_TRUE;
9258           break;
9259         }
9260       }
9261       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
9262       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
9263       for (s = 0; s < supportSize; ++s) {
9264         if (support[s] == p) break;
9265       }
9266       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
9267         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
9268         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
9269         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9270         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
9271         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
9272         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9273         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]);
9274         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
9275       }
9276     }
9277     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
9278     if (p != pp) {
9279       storagecheck = PETSC_FALSE;
9280       continue;
9281     }
9282     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
9283     PetscCall(DMPlexGetSupport(dm, p, &support));
9284     for (s = 0; s < supportSize; ++s) {
9285       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
9286       PetscCall(DMPlexGetCone(dm, support[s], &cone));
9287       for (c = 0; c < coneSize; ++c) {
9288         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
9289         if (cone[c] != pp) {
9290           c = 0;
9291           break;
9292         }
9293         if (cone[c] == p) break;
9294       }
9295       if (c >= coneSize) {
9296         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
9297         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
9298         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9299         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
9300         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
9301         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9302         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
9303       }
9304     }
9305   }
9306   if (storagecheck) {
9307     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
9308     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
9309     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
9310   }
9311   PetscFunctionReturn(PETSC_SUCCESS);
9312 }
9313 
9314 /*
9315   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.
9316 */
9317 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
9318 {
9319   DMPolytopeType  cct;
9320   PetscInt        ptpoints[4];
9321   const PetscInt *cone, *ccone, *ptcone;
9322   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
9323 
9324   PetscFunctionBegin;
9325   *unsplit = 0;
9326   switch (ct) {
9327   case DM_POLYTOPE_POINT_PRISM_TENSOR:
9328     ptpoints[npt++] = c;
9329     break;
9330   case DM_POLYTOPE_SEG_PRISM_TENSOR:
9331     PetscCall(DMPlexGetCone(dm, c, &cone));
9332     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9333     for (cp = 0; cp < coneSize; ++cp) {
9334       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
9335       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
9336     }
9337     break;
9338   case DM_POLYTOPE_TRI_PRISM_TENSOR:
9339   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9340     PetscCall(DMPlexGetCone(dm, c, &cone));
9341     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9342     for (cp = 0; cp < coneSize; ++cp) {
9343       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
9344       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
9345       for (ccp = 0; ccp < cconeSize; ++ccp) {
9346         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
9347         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
9348           PetscInt p;
9349           for (p = 0; p < npt; ++p)
9350             if (ptpoints[p] == ccone[ccp]) break;
9351           if (p == npt) ptpoints[npt++] = ccone[ccp];
9352         }
9353       }
9354     }
9355     break;
9356   default:
9357     break;
9358   }
9359   for (pt = 0; pt < npt; ++pt) {
9360     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
9361     if (ptcone[0] == ptcone[1]) ++(*unsplit);
9362   }
9363   PetscFunctionReturn(PETSC_SUCCESS);
9364 }
9365 
9366 /*@
9367   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
9368 
9369   Input Parameters:
9370 + dm         - The `DMPLEX` object
9371 - cellHeight - Normally 0
9372 
9373   Level: developer
9374 
9375   Notes:
9376   This is a useful diagnostic when creating meshes programmatically.
9377   Currently applicable only to homogeneous simplex or tensor meshes.
9378 
9379   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9380 
9381 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9382 @*/
9383 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
9384 {
9385   DMPlexInterpolatedFlag interp;
9386   DMPolytopeType         ct;
9387   PetscInt               vStart, vEnd, cStart, cEnd, c;
9388 
9389   PetscFunctionBegin;
9390   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9391   PetscCall(DMPlexIsInterpolated(dm, &interp));
9392   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9393   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9394   for (c = cStart; c < cEnd; ++c) {
9395     PetscInt *closure = NULL;
9396     PetscInt  coneSize, closureSize, cl, Nv = 0;
9397 
9398     PetscCall(DMPlexGetCellType(dm, c, &ct));
9399     if (ct == DM_POLYTOPE_UNKNOWN) continue;
9400     if (interp == DMPLEX_INTERPOLATED_FULL) {
9401       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9402       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));
9403     }
9404     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9405     for (cl = 0; cl < closureSize * 2; cl += 2) {
9406       const PetscInt p = closure[cl];
9407       if ((p >= vStart) && (p < vEnd)) ++Nv;
9408     }
9409     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9410     /* Special Case: Tensor faces with identified vertices */
9411     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
9412       PetscInt unsplit;
9413 
9414       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9415       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
9416     }
9417     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));
9418   }
9419   PetscFunctionReturn(PETSC_SUCCESS);
9420 }
9421 
9422 /*@
9423   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
9424 
9425   Collective
9426 
9427   Input Parameters:
9428 + dm         - The `DMPLEX` object
9429 - cellHeight - Normally 0
9430 
9431   Level: developer
9432 
9433   Notes:
9434   This is a useful diagnostic when creating meshes programmatically.
9435   This routine is only relevant for meshes that are fully interpolated across all ranks.
9436   It will error out if a partially interpolated mesh is given on some rank.
9437   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
9438 
9439   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9440 
9441 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
9442 @*/
9443 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
9444 {
9445   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
9446   DMPlexInterpolatedFlag interpEnum;
9447 
9448   PetscFunctionBegin;
9449   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9450   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
9451   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
9452   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
9453     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
9454     PetscFunctionReturn(PETSC_SUCCESS);
9455   }
9456 
9457   PetscCall(DMGetDimension(dm, &dim));
9458   PetscCall(DMPlexGetDepth(dm, &depth));
9459   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9460   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
9461     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
9462     for (c = cStart; c < cEnd; ++c) {
9463       const PetscInt       *cone, *ornt, *faceSizes, *faces;
9464       const DMPolytopeType *faceTypes;
9465       DMPolytopeType        ct;
9466       PetscInt              numFaces, coneSize, f;
9467       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
9468 
9469       PetscCall(DMPlexGetCellType(dm, c, &ct));
9470       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9471       if (unsplit) continue;
9472       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9473       PetscCall(DMPlexGetCone(dm, c, &cone));
9474       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
9475       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9476       for (cl = 0; cl < closureSize * 2; cl += 2) {
9477         const PetscInt p = closure[cl];
9478         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
9479       }
9480       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9481       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);
9482       for (f = 0; f < numFaces; ++f) {
9483         DMPolytopeType fct;
9484         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
9485 
9486         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
9487         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
9488         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
9489           const PetscInt p = fclosure[cl];
9490           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
9491         }
9492         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]);
9493         for (v = 0; v < fnumCorners; ++v) {
9494           if (fclosure[v] != faces[fOff + v]) {
9495             PetscInt v1;
9496 
9497             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
9498             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
9499             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
9500             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
9501             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9502             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]);
9503           }
9504         }
9505         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
9506         fOff += faceSizes[f];
9507       }
9508       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9509       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9510     }
9511   }
9512   PetscFunctionReturn(PETSC_SUCCESS);
9513 }
9514 
9515 /*@
9516   DMPlexCheckGeometry - Check the geometry of mesh cells
9517 
9518   Input Parameter:
9519 . dm - The `DMPLEX` object
9520 
9521   Level: developer
9522 
9523   Notes:
9524   This is a useful diagnostic when creating meshes programmatically.
9525 
9526   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9527 
9528 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9529 @*/
9530 PetscErrorCode DMPlexCheckGeometry(DM dm)
9531 {
9532   Vec       coordinates;
9533   PetscReal detJ, J[9], refVol = 1.0;
9534   PetscReal vol;
9535   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9536 
9537   PetscFunctionBegin;
9538   PetscCall(DMGetDimension(dm, &dim));
9539   PetscCall(DMGetCoordinateDim(dm, &dE));
9540   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9541   PetscCall(DMPlexGetDepth(dm, &depth));
9542   for (d = 0; d < dim; ++d) refVol *= 2.0;
9543   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9544   /* Make sure local coordinates are created, because that step is collective */
9545   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9546   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9547   for (c = cStart; c < cEnd; ++c) {
9548     DMPolytopeType ct;
9549     PetscInt       unsplit;
9550     PetscBool      ignoreZeroVol = PETSC_FALSE;
9551 
9552     PetscCall(DMPlexGetCellType(dm, c, &ct));
9553     switch (ct) {
9554     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9555     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9556     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9557       ignoreZeroVol = PETSC_TRUE;
9558       break;
9559     default:
9560       break;
9561     }
9562     switch (ct) {
9563     case DM_POLYTOPE_TRI_PRISM:
9564     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9565     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9566     case DM_POLYTOPE_PYRAMID:
9567       continue;
9568     default:
9569       break;
9570     }
9571     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9572     if (unsplit) continue;
9573     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9574     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);
9575     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9576     /* This should work with periodicity since DG coordinates should be used */
9577     if (depth > 1) {
9578       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9579       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);
9580       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9581     }
9582   }
9583   PetscFunctionReturn(PETSC_SUCCESS);
9584 }
9585 
9586 /*@
9587   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9588 
9589   Collective
9590 
9591   Input Parameters:
9592 + dm              - The `DMPLEX` object
9593 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9594 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9595 
9596   Level: developer
9597 
9598   Notes:
9599   This is mainly intended for debugging/testing purposes.
9600 
9601   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9602 
9603   Extra roots can come from periodic cuts, where additional points appear on the boundary
9604 
9605 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9606 @*/
9607 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9608 {
9609   PetscInt           l, nleaves, nroots, overlap;
9610   const PetscInt    *locals;
9611   const PetscSFNode *remotes;
9612   PetscBool          distributed;
9613   MPI_Comm           comm;
9614   PetscMPIInt        rank;
9615 
9616   PetscFunctionBegin;
9617   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9618   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9619   else pointSF = dm->sf;
9620   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9621   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9622   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9623   {
9624     PetscMPIInt mpiFlag;
9625 
9626     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9627     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9628   }
9629   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9630   PetscCall(DMPlexIsDistributed(dm, &distributed));
9631   if (!distributed) {
9632     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);
9633     PetscFunctionReturn(PETSC_SUCCESS);
9634   }
9635   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);
9636   PetscCall(DMPlexGetOverlap(dm, &overlap));
9637 
9638   /* Check SF graph is compatible with DMPlex chart */
9639   {
9640     PetscInt pStart, pEnd, maxLeaf;
9641 
9642     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9643     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9644     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9645     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9646   }
9647 
9648   /* Check there are no cells in interface */
9649   if (!overlap) {
9650     PetscInt cellHeight, cStart, cEnd;
9651 
9652     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9653     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9654     for (l = 0; l < nleaves; ++l) {
9655       const PetscInt point = locals ? locals[l] : l;
9656 
9657       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9658     }
9659   }
9660 
9661   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9662   {
9663     const PetscInt *rootdegree;
9664 
9665     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9666     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9667     for (l = 0; l < nleaves; ++l) {
9668       const PetscInt  point = locals ? locals[l] : l;
9669       const PetscInt *cone;
9670       PetscInt        coneSize, c, idx;
9671 
9672       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9673       PetscCall(DMPlexGetCone(dm, point, &cone));
9674       for (c = 0; c < coneSize; ++c) {
9675         if (!rootdegree[cone[c]]) {
9676           if (locals) {
9677             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9678           } else {
9679             idx = (cone[c] < nleaves) ? cone[c] : -1;
9680           }
9681           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9682         }
9683       }
9684     }
9685   }
9686   PetscFunctionReturn(PETSC_SUCCESS);
9687 }
9688 
9689 /*@
9690   DMPlexCheckOrphanVertices - Check that no vertices are disconnected from the mesh, unless the mesh only consists of disconnected vertices.
9691 
9692   Collective
9693 
9694   Input Parameter:
9695 . dm - The `DMPLEX` object
9696 
9697   Level: developer
9698 
9699   Notes:
9700   This is mainly intended for debugging/testing purposes.
9701 
9702   Other cell types which are disconnected would be caught by the symmetry and face checks.
9703 
9704   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9705 
9706 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheck()`, `DMSetFromOptions()`
9707 @*/
9708 PetscErrorCode DMPlexCheckOrphanVertices(DM dm)
9709 {
9710   PetscInt pStart, pEnd, vStart, vEnd;
9711 
9712   PetscFunctionBegin;
9713   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9714   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9715   if (pStart == vStart && pEnd == vEnd) PetscFunctionReturn(PETSC_SUCCESS);
9716   for (PetscInt v = vStart; v < vEnd; ++v) {
9717     PetscInt suppSize;
9718 
9719     PetscCall(DMPlexGetSupportSize(dm, v, &suppSize));
9720     PetscCheck(suppSize, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Vertex %" PetscInt_FMT " is disconnected from the mesh", v);
9721   }
9722   PetscFunctionReturn(PETSC_SUCCESS);
9723 }
9724 
9725 /*@
9726   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9727 
9728   Input Parameter:
9729 . dm - The `DMPLEX` object
9730 
9731   Level: developer
9732 
9733   Notes:
9734   This is a useful diagnostic when creating meshes programmatically.
9735 
9736   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9737 
9738   Currently does not include `DMPlexCheckCellShape()`.
9739 
9740 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9741 @*/
9742 PetscErrorCode DMPlexCheck(DM dm)
9743 {
9744   PetscInt cellHeight;
9745 
9746   PetscFunctionBegin;
9747   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9748   PetscCall(DMPlexCheckSymmetry(dm));
9749   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9750   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9751   PetscCall(DMPlexCheckGeometry(dm));
9752   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9753   PetscCall(DMPlexCheckInterfaceCones(dm));
9754   PetscCall(DMPlexCheckOrphanVertices(dm));
9755   PetscFunctionReturn(PETSC_SUCCESS);
9756 }
9757 
9758 typedef struct cell_stats {
9759   PetscReal min, max, sum, squaresum;
9760   PetscInt  count;
9761 } cell_stats_t;
9762 
9763 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9764 {
9765   PetscInt i, N = *len;
9766 
9767   for (i = 0; i < N; i++) {
9768     cell_stats_t *A = (cell_stats_t *)a;
9769     cell_stats_t *B = (cell_stats_t *)b;
9770 
9771     B->min = PetscMin(A->min, B->min);
9772     B->max = PetscMax(A->max, B->max);
9773     B->sum += A->sum;
9774     B->squaresum += A->squaresum;
9775     B->count += A->count;
9776   }
9777 }
9778 
9779 /*@
9780   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9781 
9782   Collective
9783 
9784   Input Parameters:
9785 + dm        - The `DMPLEX` object
9786 . output    - If true, statistics will be displayed on `stdout`
9787 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9788 
9789   Level: developer
9790 
9791   Notes:
9792   This is mainly intended for debugging/testing purposes.
9793 
9794   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9795 
9796 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9797 @*/
9798 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9799 {
9800   DM           dmCoarse;
9801   cell_stats_t stats, globalStats;
9802   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9803   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9804   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9805   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9806   PetscMPIInt  rank, size;
9807 
9808   PetscFunctionBegin;
9809   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9810   stats.min = PETSC_MAX_REAL;
9811   stats.max = PETSC_MIN_REAL;
9812   stats.sum = stats.squaresum = 0.;
9813   stats.count                 = 0;
9814 
9815   PetscCallMPI(MPI_Comm_size(comm, &size));
9816   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9817   PetscCall(DMGetCoordinateDim(dm, &cdim));
9818   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9819   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9820   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9821   for (c = cStart; c < cEnd; c++) {
9822     PetscInt  i;
9823     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9824 
9825     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9826     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9827     for (i = 0; i < PetscSqr(cdim); ++i) {
9828       frobJ += J[i] * J[i];
9829       frobInvJ += invJ[i] * invJ[i];
9830     }
9831     cond2 = frobJ * frobInvJ;
9832     cond  = PetscSqrtReal(cond2);
9833 
9834     stats.min = PetscMin(stats.min, cond);
9835     stats.max = PetscMax(stats.max, cond);
9836     stats.sum += cond;
9837     stats.squaresum += cond2;
9838     stats.count++;
9839     if (output && cond > limit) {
9840       PetscSection coordSection;
9841       Vec          coordsLocal;
9842       PetscScalar *coords = NULL;
9843       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9844 
9845       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9846       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9847       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9848       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9849       for (i = 0; i < Nv / cdim; ++i) {
9850         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9851         for (d = 0; d < cdim; ++d) {
9852           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9853           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9854         }
9855         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9856       }
9857       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9858       for (cl = 0; cl < clSize * 2; cl += 2) {
9859         const PetscInt edge = closure[cl];
9860 
9861         if ((edge >= eStart) && (edge < eEnd)) {
9862           PetscReal len;
9863 
9864           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9865           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9866         }
9867       }
9868       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9869       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9870     }
9871   }
9872   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9873 
9874   if (size > 1) {
9875     PetscMPIInt  blockLengths[2] = {4, 1};
9876     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9877     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9878     MPI_Op       statReduce;
9879 
9880     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9881     PetscCallMPI(MPI_Type_commit(&statType));
9882     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9883     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9884     PetscCallMPI(MPI_Op_free(&statReduce));
9885     PetscCallMPI(MPI_Type_free(&statType));
9886   } else {
9887     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9888   }
9889   if (rank == 0) {
9890     count = globalStats.count;
9891     min   = globalStats.min;
9892     max   = globalStats.max;
9893     mean  = globalStats.sum / globalStats.count;
9894     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9895   }
9896 
9897   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));
9898   PetscCall(PetscFree2(J, invJ));
9899 
9900   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9901   if (dmCoarse) {
9902     PetscBool isplex;
9903 
9904     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9905     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9906   }
9907   PetscFunctionReturn(PETSC_SUCCESS);
9908 }
9909 
9910 /*@
9911   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9912   orthogonal quality below given tolerance.
9913 
9914   Collective
9915 
9916   Input Parameters:
9917 + dm   - The `DMPLEX` object
9918 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9919 - atol - [0, 1] Absolute tolerance for tagging cells.
9920 
9921   Output Parameters:
9922 + OrthQual      - `Vec` containing orthogonal quality per cell
9923 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9924 
9925   Options Database Keys:
9926 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9927 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9928 
9929   Level: intermediate
9930 
9931   Notes:
9932   Orthogonal quality is given by the following formula\:
9933 
9934   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9935 
9936   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
9937   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9938   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9939   calculating the cosine of the angle between these vectors.
9940 
9941   Orthogonal quality ranges from 1 (best) to 0 (worst).
9942 
9943   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9944   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9945 
9946   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9947 
9948 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9949 @*/
9950 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PeOp PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9951 {
9952   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9953   PetscInt              *idx;
9954   PetscScalar           *oqVals;
9955   const PetscScalar     *cellGeomArr, *faceGeomArr;
9956   PetscReal             *ci, *fi, *Ai;
9957   MPI_Comm               comm;
9958   Vec                    cellgeom, facegeom;
9959   DM                     dmFace, dmCell;
9960   IS                     glob;
9961   ISLocalToGlobalMapping ltog;
9962   PetscViewer            vwr;
9963 
9964   PetscFunctionBegin;
9965   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9966   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9967   PetscAssertPointer(OrthQual, 4);
9968   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9969   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9970   PetscCall(DMGetDimension(dm, &nc));
9971   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9972   {
9973     DMPlexInterpolatedFlag interpFlag;
9974 
9975     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9976     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9977       PetscMPIInt rank;
9978 
9979       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9980       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9981     }
9982   }
9983   if (OrthQualLabel) {
9984     PetscAssertPointer(OrthQualLabel, 5);
9985     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9986     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9987   } else {
9988     *OrthQualLabel = NULL;
9989   }
9990   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9991   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9992   PetscCall(DMPlexCreateCellNumbering(dm, PETSC_TRUE, &glob));
9993   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9994   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9995   PetscCall(VecCreate(comm, OrthQual));
9996   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9997   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9998   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9999   PetscCall(VecSetUp(*OrthQual));
10000   PetscCall(ISDestroy(&glob));
10001   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
10002   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
10003   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
10004   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
10005   PetscCall(VecGetDM(cellgeom, &dmCell));
10006   PetscCall(VecGetDM(facegeom, &dmFace));
10007   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
10008   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
10009     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
10010     PetscInt         cellarr[2], *adj = NULL;
10011     PetscScalar     *cArr, *fArr;
10012     PetscReal        minvalc = 1.0, minvalf = 1.0;
10013     PetscFVCellGeom *cg;
10014 
10015     idx[cellIter] = cell - cStart;
10016     cellarr[0]    = cell;
10017     /* Make indexing into cellGeom easier */
10018     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
10019     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
10020     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
10021     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
10022     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
10023       PetscInt         i;
10024       const PetscInt   neigh  = adj[cellneigh];
10025       PetscReal        normci = 0, normfi = 0, normai = 0;
10026       PetscFVCellGeom *cgneigh;
10027       PetscFVFaceGeom *fg;
10028 
10029       /* Don't count ourselves in the neighbor list */
10030       if (neigh == cell) continue;
10031       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
10032       cellarr[1] = neigh;
10033       {
10034         PetscInt        numcovpts;
10035         const PetscInt *covpts;
10036 
10037         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
10038         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
10039         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
10040       }
10041 
10042       /* Compute c_i, f_i and their norms */
10043       for (i = 0; i < nc; i++) {
10044         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
10045         fi[i] = fg->centroid[i] - cg->centroid[i];
10046         Ai[i] = fg->normal[i];
10047         normci += PetscPowReal(ci[i], 2);
10048         normfi += PetscPowReal(fi[i], 2);
10049         normai += PetscPowReal(Ai[i], 2);
10050       }
10051       normci = PetscSqrtReal(normci);
10052       normfi = PetscSqrtReal(normfi);
10053       normai = PetscSqrtReal(normai);
10054 
10055       /* Normalize and compute for each face-cell-normal pair */
10056       for (i = 0; i < nc; i++) {
10057         ci[i] = ci[i] / normci;
10058         fi[i] = fi[i] / normfi;
10059         Ai[i] = Ai[i] / normai;
10060         /* PetscAbs because I don't know if normals are guaranteed to point out */
10061         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
10062         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
10063       }
10064       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
10065       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
10066     }
10067     PetscCall(PetscFree(adj));
10068     PetscCall(PetscFree2(cArr, fArr));
10069     /* Defer to cell if they're equal */
10070     oqVals[cellIter] = PetscMin(minvalf, minvalc);
10071     if (OrthQualLabel) {
10072       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
10073     }
10074   }
10075   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
10076   PetscCall(VecAssemblyBegin(*OrthQual));
10077   PetscCall(VecAssemblyEnd(*OrthQual));
10078   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
10079   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
10080   PetscCall(PetscOptionsCreateViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
10081   if (OrthQualLabel) {
10082     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
10083   }
10084   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
10085   PetscCall(PetscViewerDestroy(&vwr));
10086   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
10087   PetscFunctionReturn(PETSC_SUCCESS);
10088 }
10089 
10090 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
10091  * interpolator construction */
10092 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
10093 {
10094   PetscSection section, newSection, gsection;
10095   PetscSF      sf;
10096   PetscBool    hasConstraints, ghasConstraints;
10097 
10098   PetscFunctionBegin;
10099   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10100   PetscAssertPointer(odm, 2);
10101   PetscCall(DMGetLocalSection(dm, &section));
10102   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
10103   PetscCallMPI(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
10104   if (!ghasConstraints) {
10105     PetscCall(PetscObjectReference((PetscObject)dm));
10106     *odm = dm;
10107     PetscFunctionReturn(PETSC_SUCCESS);
10108   }
10109   PetscCall(DMClone(dm, odm));
10110   PetscCall(DMCopyFields(dm, PETSC_DETERMINE, PETSC_DETERMINE, *odm));
10111   PetscCall(DMGetLocalSection(*odm, &newSection));
10112   PetscCall(DMGetPointSF(*odm, &sf));
10113   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_TRUE, PETSC_FALSE, &gsection));
10114   PetscCall(DMSetGlobalSection(*odm, gsection));
10115   PetscCall(PetscSectionDestroy(&gsection));
10116   PetscFunctionReturn(PETSC_SUCCESS);
10117 }
10118 
10119 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
10120 {
10121   DM        dmco, dmfo;
10122   Mat       interpo;
10123   Vec       rscale;
10124   Vec       cglobalo, clocal;
10125   Vec       fglobal, fglobalo, flocal;
10126   PetscBool regular;
10127 
10128   PetscFunctionBegin;
10129   PetscCall(DMGetFullDM(dmc, &dmco));
10130   PetscCall(DMGetFullDM(dmf, &dmfo));
10131   PetscCall(DMSetCoarseDM(dmfo, dmco));
10132   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
10133   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
10134   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
10135   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
10136   PetscCall(DMCreateLocalVector(dmc, &clocal));
10137   PetscCall(VecSet(cglobalo, 0.));
10138   PetscCall(VecSet(clocal, 0.));
10139   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
10140   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
10141   PetscCall(DMCreateLocalVector(dmf, &flocal));
10142   PetscCall(VecSet(fglobal, 0.));
10143   PetscCall(VecSet(fglobalo, 0.));
10144   PetscCall(VecSet(flocal, 0.));
10145   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
10146   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
10147   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
10148   PetscCall(MatMult(interpo, cglobalo, fglobalo));
10149   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
10150   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
10151   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
10152   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
10153   *shift = fglobal;
10154   PetscCall(VecDestroy(&flocal));
10155   PetscCall(VecDestroy(&fglobalo));
10156   PetscCall(VecDestroy(&clocal));
10157   PetscCall(VecDestroy(&cglobalo));
10158   PetscCall(VecDestroy(&rscale));
10159   PetscCall(MatDestroy(&interpo));
10160   PetscCall(DMDestroy(&dmfo));
10161   PetscCall(DMDestroy(&dmco));
10162   PetscFunctionReturn(PETSC_SUCCESS);
10163 }
10164 
10165 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
10166 {
10167   PetscObject shifto;
10168   Vec         shift;
10169 
10170   PetscFunctionBegin;
10171   if (!interp) {
10172     Vec rscale;
10173 
10174     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
10175     PetscCall(VecDestroy(&rscale));
10176   } else {
10177     PetscCall(PetscObjectReference((PetscObject)interp));
10178   }
10179   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
10180   if (!shifto) {
10181     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
10182     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
10183     shifto = (PetscObject)shift;
10184     PetscCall(VecDestroy(&shift));
10185   }
10186   shift = (Vec)shifto;
10187   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
10188   PetscCall(VecAXPY(fineSol, 1.0, shift));
10189   PetscCall(MatDestroy(&interp));
10190   PetscFunctionReturn(PETSC_SUCCESS);
10191 }
10192 
10193 /* Pointwise interpolation
10194      Just code FEM for now
10195      u^f = I u^c
10196      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
10197      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
10198      I_{ij} = psi^f_i phi^c_j
10199 */
10200 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
10201 {
10202   PetscSection gsc, gsf;
10203   PetscInt     m, n;
10204   void        *ctx;
10205   DM           cdm;
10206   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
10207 
10208   PetscFunctionBegin;
10209   PetscCall(DMGetGlobalSection(dmFine, &gsf));
10210   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10211   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10212   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10213 
10214   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
10215   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
10216   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10217   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
10218   PetscCall(DMGetApplicationContext(dmFine, &ctx));
10219 
10220   PetscCall(DMGetCoarseDM(dmFine, &cdm));
10221   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10222   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
10223   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
10224   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
10225   if (scaling) {
10226     /* Use naive scaling */
10227     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
10228   }
10229   PetscFunctionReturn(PETSC_SUCCESS);
10230 }
10231 
10232 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
10233 {
10234   VecScatter ctx;
10235 
10236   PetscFunctionBegin;
10237   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
10238   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
10239   PetscCall(VecScatterDestroy(&ctx));
10240   PetscFunctionReturn(PETSC_SUCCESS);
10241 }
10242 
10243 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[])
10244 {
10245   const PetscInt f  = (PetscInt)PetscRealPart(constants[numConstants]);
10246   const PetscInt Nc = uOff[f + 1] - uOff[f];
10247   for (PetscInt c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
10248 }
10249 
10250 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *lmass, Vec *mass)
10251 {
10252   DM           dmc;
10253   PetscDS      ds;
10254   Vec          ones, locmass;
10255   IS           cellIS;
10256   PetscFormKey key;
10257   PetscInt     depth;
10258 
10259   PetscFunctionBegin;
10260   PetscCall(DMClone(dm, &dmc));
10261   PetscCall(DMCopyDisc(dm, dmc));
10262   PetscCall(DMGetDS(dmc, &ds));
10263   for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10264   if (mass) PetscCall(DMCreateGlobalVector(dm, mass));
10265   if (lmass) PetscCall(DMCreateLocalVector(dm, &locmass));
10266   else PetscCall(DMGetLocalVector(dm, &locmass));
10267   PetscCall(DMGetLocalVector(dm, &ones));
10268   PetscCall(DMPlexGetDepth(dm, &depth));
10269   PetscCall(DMGetStratumIS(dm, "depth", depth, &cellIS));
10270   PetscCall(VecSet(locmass, 0.0));
10271   PetscCall(VecSet(ones, 1.0));
10272   key.label = NULL;
10273   key.value = 0;
10274   key.field = 0;
10275   key.part  = 0;
10276   PetscCall(DMPlexComputeJacobianActionByKey(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
10277   PetscCall(ISDestroy(&cellIS));
10278   if (mass) {
10279     PetscCall(DMLocalToGlobalBegin(dm, locmass, ADD_VALUES, *mass));
10280     PetscCall(DMLocalToGlobalEnd(dm, locmass, ADD_VALUES, *mass));
10281   }
10282   PetscCall(DMRestoreLocalVector(dm, &ones));
10283   if (lmass) *lmass = locmass;
10284   else PetscCall(DMRestoreLocalVector(dm, &locmass));
10285   PetscCall(DMDestroy(&dmc));
10286   PetscFunctionReturn(PETSC_SUCCESS);
10287 }
10288 
10289 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
10290 {
10291   PetscSection gsc, gsf;
10292   PetscInt     m, n;
10293   void        *ctx;
10294   DM           cdm;
10295   PetscBool    regular;
10296 
10297   PetscFunctionBegin;
10298   if (dmFine == dmCoarse) {
10299     DM            dmc;
10300     PetscDS       ds;
10301     PetscWeakForm wf;
10302     Vec           u;
10303     IS            cellIS;
10304     PetscFormKey  key;
10305     PetscInt      depth;
10306 
10307     PetscCall(DMClone(dmFine, &dmc));
10308     PetscCall(DMCopyDisc(dmFine, dmc));
10309     PetscCall(DMGetDS(dmc, &ds));
10310     PetscCall(PetscDSGetWeakForm(ds, &wf));
10311     PetscCall(PetscWeakFormClear(wf));
10312     for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10313     PetscCall(DMCreateMatrix(dmc, mass));
10314     PetscCall(DMGetLocalVector(dmc, &u));
10315     PetscCall(DMPlexGetDepth(dmc, &depth));
10316     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
10317     PetscCall(MatZeroEntries(*mass));
10318     key.label = NULL;
10319     key.value = 0;
10320     key.field = 0;
10321     key.part  = 0;
10322     PetscCall(DMPlexComputeJacobianByKey(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
10323     PetscCall(ISDestroy(&cellIS));
10324     PetscCall(DMRestoreLocalVector(dmc, &u));
10325     PetscCall(DMDestroy(&dmc));
10326   } else {
10327     PetscCall(DMGetGlobalSection(dmFine, &gsf));
10328     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10329     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10330     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10331 
10332     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
10333     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10334     PetscCall(MatSetType(*mass, dmCoarse->mattype));
10335     PetscCall(DMGetApplicationContext(dmFine, &ctx));
10336 
10337     PetscCall(DMGetCoarseDM(dmFine, &cdm));
10338     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10339     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
10340     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
10341   }
10342   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
10343   PetscFunctionReturn(PETSC_SUCCESS);
10344 }
10345 
10346 /*@
10347   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10348 
10349   Input Parameter:
10350 . dm - The `DMPLEX` object
10351 
10352   Output Parameter:
10353 . regular - The flag
10354 
10355   Level: intermediate
10356 
10357 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
10358 @*/
10359 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
10360 {
10361   PetscFunctionBegin;
10362   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10363   PetscAssertPointer(regular, 2);
10364   *regular = ((DM_Plex *)dm->data)->regularRefinement;
10365   PetscFunctionReturn(PETSC_SUCCESS);
10366 }
10367 
10368 /*@
10369   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10370 
10371   Input Parameters:
10372 + dm      - The `DMPLEX` object
10373 - regular - The flag
10374 
10375   Level: intermediate
10376 
10377 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
10378 @*/
10379 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
10380 {
10381   PetscFunctionBegin;
10382   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10383   ((DM_Plex *)dm->data)->regularRefinement = regular;
10384   PetscFunctionReturn(PETSC_SUCCESS);
10385 }
10386 
10387 /*@
10388   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
10389   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
10390 
10391   Not Collective
10392 
10393   Input Parameter:
10394 . dm - The `DMPLEX` object
10395 
10396   Output Parameters:
10397 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
10398 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
10399 
10400   Level: intermediate
10401 
10402 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
10403 @*/
10404 PetscErrorCode DMPlexGetAnchors(DM dm, PeOp PetscSection *anchorSection, PeOp IS *anchorIS)
10405 {
10406   DM_Plex *plex = (DM_Plex *)dm->data;
10407 
10408   PetscFunctionBegin;
10409   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10410   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
10411   if (anchorSection) *anchorSection = plex->anchorSection;
10412   if (anchorIS) *anchorIS = plex->anchorIS;
10413   PetscFunctionReturn(PETSC_SUCCESS);
10414 }
10415 
10416 /*@
10417   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
10418 
10419   Collective
10420 
10421   Input Parameters:
10422 + dm            - The `DMPLEX` object
10423 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
10424                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10425 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10426 
10427   Level: intermediate
10428 
10429   Notes:
10430   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
10431   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
10432   combination of other points' degrees of freedom.
10433 
10434   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
10435   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
10436 
10437   The reference counts of `anchorSection` and `anchorIS` are incremented.
10438 
10439 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
10440 @*/
10441 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
10442 {
10443   DM_Plex    *plex = (DM_Plex *)dm->data;
10444   PetscMPIInt result;
10445 
10446   PetscFunctionBegin;
10447   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10448   if (anchorSection) {
10449     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
10450     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
10451     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
10452   }
10453   if (anchorIS) {
10454     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
10455     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
10456     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
10457   }
10458 
10459   PetscCall(PetscObjectReference((PetscObject)anchorSection));
10460   PetscCall(PetscSectionDestroy(&plex->anchorSection));
10461   plex->anchorSection = anchorSection;
10462 
10463   PetscCall(PetscObjectReference((PetscObject)anchorIS));
10464   PetscCall(ISDestroy(&plex->anchorIS));
10465   plex->anchorIS = anchorIS;
10466 
10467   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
10468     PetscInt        size, a, pStart, pEnd;
10469     const PetscInt *anchors;
10470 
10471     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10472     PetscCall(ISGetLocalSize(anchorIS, &size));
10473     PetscCall(ISGetIndices(anchorIS, &anchors));
10474     for (a = 0; a < size; a++) {
10475       PetscInt p;
10476 
10477       p = anchors[a];
10478       if (p >= pStart && p < pEnd) {
10479         PetscInt dof;
10480 
10481         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10482         if (dof) {
10483           PetscCall(ISRestoreIndices(anchorIS, &anchors));
10484           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
10485         }
10486       }
10487     }
10488     PetscCall(ISRestoreIndices(anchorIS, &anchors));
10489   }
10490   /* reset the generic constraints */
10491   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
10492   PetscFunctionReturn(PETSC_SUCCESS);
10493 }
10494 
10495 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
10496 {
10497   PetscSection anchorSection;
10498   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
10499 
10500   PetscFunctionBegin;
10501   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10502   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10503   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
10504   PetscCall(PetscSectionGetNumFields(section, &numFields));
10505   if (numFields) {
10506     PetscInt f;
10507     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
10508 
10509     for (f = 0; f < numFields; f++) {
10510       PetscInt numComp;
10511 
10512       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
10513       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
10514     }
10515   }
10516   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10517   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10518   pStart = PetscMax(pStart, sStart);
10519   pEnd   = PetscMin(pEnd, sEnd);
10520   pEnd   = PetscMax(pStart, pEnd);
10521   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
10522   for (p = pStart; p < pEnd; p++) {
10523     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10524     if (dof) {
10525       PetscCall(PetscSectionGetDof(section, p, &dof));
10526       PetscCall(PetscSectionSetDof(*cSec, p, dof));
10527       for (f = 0; f < numFields; f++) {
10528         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
10529         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
10530       }
10531     }
10532   }
10533   PetscCall(PetscSectionSetUp(*cSec));
10534   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
10535   PetscFunctionReturn(PETSC_SUCCESS);
10536 }
10537 
10538 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
10539 {
10540   PetscSection    aSec;
10541   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
10542   const PetscInt *anchors;
10543   PetscInt        numFields, f;
10544   IS              aIS;
10545   MatType         mtype;
10546   PetscBool       iscuda, iskokkos;
10547 
10548   PetscFunctionBegin;
10549   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10550   PetscCall(PetscSectionGetStorageSize(cSec, &m));
10551   PetscCall(PetscSectionGetStorageSize(section, &n));
10552   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
10553   PetscCall(MatSetSizes(*cMat, m, n, m, n));
10554   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
10555   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
10556   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
10557   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
10558   if (iscuda) mtype = MATSEQAIJCUSPARSE;
10559   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
10560   else mtype = MATSEQAIJ;
10561   PetscCall(MatSetType(*cMat, mtype));
10562   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
10563   PetscCall(ISGetIndices(aIS, &anchors));
10564   /* cSec will be a subset of aSec and section */
10565   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
10566   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10567   PetscCall(PetscMalloc1(m + 1, &i));
10568   i[0] = 0;
10569   PetscCall(PetscSectionGetNumFields(section, &numFields));
10570   for (p = pStart; p < pEnd; p++) {
10571     PetscInt rDof, rOff, r;
10572 
10573     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10574     if (!rDof) continue;
10575     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10576     if (numFields) {
10577       for (f = 0; f < numFields; f++) {
10578         annz = 0;
10579         for (r = 0; r < rDof; r++) {
10580           a = anchors[rOff + r];
10581           if (a < sStart || a >= sEnd) continue;
10582           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10583           annz += aDof;
10584         }
10585         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10586         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10587         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10588       }
10589     } else {
10590       annz = 0;
10591       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10592       for (q = 0; q < dof; q++) {
10593         a = anchors[rOff + q];
10594         if (a < sStart || a >= sEnd) continue;
10595         PetscCall(PetscSectionGetDof(section, a, &aDof));
10596         annz += aDof;
10597       }
10598       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10599       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10600       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10601     }
10602   }
10603   nnz = i[m];
10604   PetscCall(PetscMalloc1(nnz, &j));
10605   offset = 0;
10606   for (p = pStart; p < pEnd; p++) {
10607     if (numFields) {
10608       for (f = 0; f < numFields; f++) {
10609         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10610         for (q = 0; q < dof; q++) {
10611           PetscInt rDof, rOff, r;
10612           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10613           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10614           for (r = 0; r < rDof; r++) {
10615             PetscInt s;
10616 
10617             a = anchors[rOff + r];
10618             if (a < sStart || a >= sEnd) continue;
10619             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10620             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10621             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10622           }
10623         }
10624       }
10625     } else {
10626       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10627       for (q = 0; q < dof; q++) {
10628         PetscInt rDof, rOff, r;
10629         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10630         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10631         for (r = 0; r < rDof; r++) {
10632           PetscInt s;
10633 
10634           a = anchors[rOff + r];
10635           if (a < sStart || a >= sEnd) continue;
10636           PetscCall(PetscSectionGetDof(section, a, &aDof));
10637           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10638           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10639         }
10640       }
10641     }
10642   }
10643   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10644   PetscCall(PetscFree(i));
10645   PetscCall(PetscFree(j));
10646   PetscCall(ISRestoreIndices(aIS, &anchors));
10647   PetscFunctionReturn(PETSC_SUCCESS);
10648 }
10649 
10650 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10651 {
10652   DM_Plex     *plex = (DM_Plex *)dm->data;
10653   PetscSection anchorSection, section, cSec;
10654   Mat          cMat;
10655 
10656   PetscFunctionBegin;
10657   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10658   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10659   if (anchorSection) {
10660     PetscInt Nf;
10661 
10662     PetscCall(DMGetLocalSection(dm, &section));
10663     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10664     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10665     PetscCall(DMGetNumFields(dm, &Nf));
10666     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10667     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10668     PetscCall(PetscSectionDestroy(&cSec));
10669     PetscCall(MatDestroy(&cMat));
10670   }
10671   PetscFunctionReturn(PETSC_SUCCESS);
10672 }
10673 
10674 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10675 {
10676   IS           subis;
10677   PetscSection section, subsection;
10678 
10679   PetscFunctionBegin;
10680   PetscCall(DMGetLocalSection(dm, &section));
10681   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10682   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10683   /* Create subdomain */
10684   PetscCall(DMPlexFilter(dm, label, value, PETSC_FALSE, PETSC_FALSE, NULL, subdm));
10685   /* Create submodel */
10686   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10687   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10688   PetscCall(DMSetLocalSection(*subdm, subsection));
10689   PetscCall(PetscSectionDestroy(&subsection));
10690   PetscCall(DMCopyDisc(dm, *subdm));
10691   /* Create map from submodel to global model */
10692   if (is) {
10693     PetscSection    sectionGlobal, subsectionGlobal;
10694     IS              spIS;
10695     const PetscInt *spmap;
10696     PetscInt       *subIndices;
10697     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10698     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10699 
10700     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10701     PetscCall(ISGetIndices(spIS, &spmap));
10702     PetscCall(PetscSectionGetNumFields(section, &Nf));
10703     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10704     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10705     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10706     for (p = pStart; p < pEnd; ++p) {
10707       PetscInt gdof, pSubSize = 0;
10708 
10709       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10710       if (gdof > 0) {
10711         for (f = 0; f < Nf; ++f) {
10712           PetscInt fdof, fcdof;
10713 
10714           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10715           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10716           pSubSize += fdof - fcdof;
10717         }
10718         subSize += pSubSize;
10719         if (pSubSize) {
10720           if (bs < 0) {
10721             bs = pSubSize;
10722           } else if (bs != pSubSize) {
10723             /* Layout does not admit a pointwise block size */
10724             bs = 1;
10725           }
10726         }
10727       }
10728     }
10729     /* Must have same blocksize on all procs (some might have no points) */
10730     bsLocal[0] = bs < 0 ? PETSC_INT_MAX : bs;
10731     bsLocal[1] = bs;
10732     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10733     if (bsMinMax[0] != bsMinMax[1]) {
10734       bs = 1;
10735     } else {
10736       bs = bsMinMax[0];
10737     }
10738     PetscCall(PetscMalloc1(subSize, &subIndices));
10739     for (p = pStart; p < pEnd; ++p) {
10740       PetscInt gdof, goff;
10741 
10742       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10743       if (gdof > 0) {
10744         const PetscInt point = spmap[p];
10745 
10746         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10747         for (f = 0; f < Nf; ++f) {
10748           PetscInt fdof, fcdof, fc, f2, poff = 0;
10749 
10750           /* Can get rid of this loop by storing field information in the global section */
10751           for (f2 = 0; f2 < f; ++f2) {
10752             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10753             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10754             poff += fdof - fcdof;
10755           }
10756           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10757           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10758           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10759         }
10760       }
10761     }
10762     PetscCall(ISRestoreIndices(spIS, &spmap));
10763     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10764     if (bs > 1) {
10765       /* We need to check that the block size does not come from non-contiguous fields */
10766       PetscInt i, j, set = 1;
10767       for (i = 0; i < subSize; i += bs) {
10768         for (j = 0; j < bs; ++j) {
10769           if (subIndices[i + j] != subIndices[i] + j) {
10770             set = 0;
10771             break;
10772           }
10773         }
10774       }
10775       if (set) PetscCall(ISSetBlockSize(*is, bs));
10776     }
10777     /* Attach nullspace */
10778     for (f = 0; f < Nf; ++f) {
10779       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10780       if ((*subdm)->nullspaceConstructors[f]) break;
10781     }
10782     if (f < Nf) {
10783       MatNullSpace nullSpace;
10784       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10785 
10786       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10787       PetscCall(MatNullSpaceDestroy(&nullSpace));
10788     }
10789   }
10790   PetscFunctionReturn(PETSC_SUCCESS);
10791 }
10792 
10793 /*@
10794   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10795 
10796   Input Parameters:
10797 + dm    - The `DM`
10798 - dummy - unused argument
10799 
10800   Options Database Key:
10801 . -dm_plex_monitor_throughput - Activate the monitor
10802 
10803   Level: developer
10804 
10805 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10806 @*/
10807 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10808 {
10809   PetscLogHandler default_handler;
10810 
10811   PetscFunctionBegin;
10812   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10813   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10814   if (default_handler) {
10815     PetscLogEvent      event;
10816     PetscEventPerfInfo eventInfo;
10817     PetscLogDouble     cellRate, flopRate;
10818     PetscInt           cStart, cEnd, Nf, N;
10819     const char        *name;
10820 
10821     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10822     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10823     PetscCall(DMGetNumFields(dm, &Nf));
10824     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10825     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10826     N        = (cEnd - cStart) * Nf * eventInfo.count;
10827     flopRate = eventInfo.flops / eventInfo.time;
10828     cellRate = N / eventInfo.time;
10829     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));
10830   } else {
10831     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.");
10832   }
10833   PetscFunctionReturn(PETSC_SUCCESS);
10834 }
10835