xref: /petsc/src/dm/impls/plex/plex.c (revision 7f10eacf5ba5e5e4cf7c42208c5ed153880f1d2b)
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 
12 /* Logging support */
13 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_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;
14 PetscLogEvent DMPLEX_RebalBuildGraph, DMPLEX_RebalRewriteSF, DMPLEX_RebalGatherGraph, DMPLEX_RebalPartition, DMPLEX_RebalScatterPart;
15 
16 PETSC_EXTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
17 
18 /*@
19   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
20 
21   Input Parameter:
22 . dm      - The DMPlex object
23 
24   Output Parameter:
25 . simplex - Flag checking for a simplex
26 
27   Note: This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
28   If the mesh has no cells, this returns PETSC_FALSE.
29 
30   Level: intermediate
31 
32 .seealso `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
33 @*/
34 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
35 {
36   DMPolytopeType ct;
37   PetscInt       cStart, cEnd;
38 
39   PetscFunctionBegin;
40   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
41   if (cEnd <= cStart) {
42     *simplex = PETSC_FALSE;
43     PetscFunctionReturn(0);
44   }
45   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
46   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
47   PetscFunctionReturn(0);
48 }
49 
50 /*@
51   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
52 
53   Input Parameters:
54 + dm     - The DMPlex object
55 - height - The cell height in the Plex, 0 is the default
56 
57   Output Parameters:
58 + cStart - The first "normal" cell
59 - cEnd   - The upper bound on "normal"" cells
60 
61   Note: This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
62 
63   Level: developer
64 
65 .seealso `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
66 @*/
67 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd)
68 {
69   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
70   PetscInt       cS, cE, c;
71 
72   PetscFunctionBegin;
73   PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), &cS, &cE));
74   for (c = cS; c < cE; ++c) {
75     DMPolytopeType cct;
76 
77     PetscCall(DMPlexGetCellType(dm, c, &cct));
78     if ((PetscInt)cct < 0) break;
79     switch (cct) {
80     case DM_POLYTOPE_POINT:
81     case DM_POLYTOPE_SEGMENT:
82     case DM_POLYTOPE_TRIANGLE:
83     case DM_POLYTOPE_QUADRILATERAL:
84     case DM_POLYTOPE_TETRAHEDRON:
85     case DM_POLYTOPE_HEXAHEDRON:
86       ct = cct;
87       break;
88     default:
89       break;
90     }
91     if (ct != DM_POLYTOPE_UNKNOWN) break;
92   }
93   if (ct != DM_POLYTOPE_UNKNOWN) {
94     DMLabel ctLabel;
95 
96     PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
97     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &cS, &cE));
98     // Reset label for fast lookup
99     PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
100   }
101   if (cStart) *cStart = cS;
102   if (cEnd) *cEnd = cE;
103   PetscFunctionReturn(0);
104 }
105 
106 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
107 {
108   PetscInt cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
109   PetscInt vcdof[2] = {0, 0}, globalvcdof[2];
110 
111   PetscFunctionBegin;
112   *ft = PETSC_VTK_INVALID;
113   PetscCall(DMGetCoordinateDim(dm, &cdim));
114   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
115   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
116   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
117   if (field >= 0) {
118     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
119     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
120   } else {
121     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
122     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
123   }
124   PetscCallMPI(MPI_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
125   if (globalvcdof[0]) {
126     *sStart = vStart;
127     *sEnd   = vEnd;
128     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
129     else *ft = PETSC_VTK_POINT_FIELD;
130   } else if (globalvcdof[1]) {
131     *sStart = cStart;
132     *sEnd   = cEnd;
133     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
134     else *ft = PETSC_VTK_CELL_FIELD;
135   } else {
136     if (field >= 0) {
137       const char *fieldname;
138 
139       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
140       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
141     } else {
142       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
143     }
144   }
145   PetscFunctionReturn(0);
146 }
147 
148 /*@
149   DMPlexVecView1D - Plot many 1D solutions on the same line graph
150 
151   Collective on dm
152 
153   Input Parameters:
154 + dm - The DMPlex
155 . n  - The number of vectors
156 . u  - The array of local vectors
157 - viewer - The Draw viewer
158 
159   Level: advanced
160 
161 .seealso: `VecViewFromOptions()`, `VecView()`
162 @*/
163 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
164 {
165   PetscDS            ds;
166   PetscDraw          draw = NULL;
167   PetscDrawLG        lg;
168   Vec                coordinates;
169   const PetscScalar *coords, **sol;
170   PetscReal         *vals;
171   PetscInt          *Nc;
172   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
173   char             **names;
174 
175   PetscFunctionBegin;
176   PetscCall(DMGetDS(dm, &ds));
177   PetscCall(PetscDSGetNumFields(ds, &Nf));
178   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
179   PetscCall(PetscDSGetComponents(ds, &Nc));
180 
181   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
182   if (!draw) PetscFunctionReturn(0);
183   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
184 
185   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
186   for (i = 0, l = 0; i < n; ++i) {
187     const char *vname;
188 
189     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
190     for (f = 0; f < Nf; ++f) {
191       PetscObject disc;
192       const char *fname;
193       char        tmpname[PETSC_MAX_PATH_LEN];
194 
195       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
196       /* TODO Create names for components */
197       for (c = 0; c < Nc[f]; ++c, ++l) {
198         PetscCall(PetscObjectGetName(disc, &fname));
199         PetscCall(PetscStrcpy(tmpname, vname));
200         PetscCall(PetscStrlcat(tmpname, ":", PETSC_MAX_PATH_LEN));
201         PetscCall(PetscStrlcat(tmpname, fname, PETSC_MAX_PATH_LEN));
202         PetscCall(PetscStrallocpy(tmpname, &names[l]));
203       }
204     }
205   }
206   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
207   /* Just add P_1 support for now */
208   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
209   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
210   PetscCall(VecGetArrayRead(coordinates, &coords));
211   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
212   for (v = vStart; v < vEnd; ++v) {
213     PetscScalar *x, *svals;
214 
215     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
216     for (i = 0; i < n; ++i) {
217       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
218       for (l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
219     }
220     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
221   }
222   PetscCall(VecRestoreArrayRead(coordinates, &coords));
223   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
224   for (l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
225   PetscCall(PetscFree3(sol, names, vals));
226 
227   PetscCall(PetscDrawLGDraw(lg));
228   PetscCall(PetscDrawLGDestroy(&lg));
229   PetscFunctionReturn(0);
230 }
231 
232 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
233 {
234   DM dm;
235 
236   PetscFunctionBegin;
237   PetscCall(VecGetDM(u, &dm));
238   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
239   PetscFunctionReturn(0);
240 }
241 
242 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
243 {
244   DM                 dm;
245   PetscSection       s;
246   PetscDraw          draw, popup;
247   DM                 cdm;
248   PetscSection       coordSection;
249   Vec                coordinates;
250   const PetscScalar *coords, *array;
251   PetscReal          bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
252   PetscReal          vbound[2], time;
253   PetscBool          flg;
254   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
255   const char        *name;
256   char               title[PETSC_MAX_PATH_LEN];
257 
258   PetscFunctionBegin;
259   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
260   PetscCall(VecGetDM(v, &dm));
261   PetscCall(DMGetCoordinateDim(dm, &dim));
262   PetscCall(DMGetLocalSection(dm, &s));
263   PetscCall(PetscSectionGetNumFields(s, &Nf));
264   PetscCall(DMGetCoarsenLevel(dm, &level));
265   PetscCall(DMGetCoordinateDM(dm, &cdm));
266   PetscCall(DMGetLocalSection(cdm, &coordSection));
267   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
268   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
269   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
270 
271   PetscCall(PetscObjectGetName((PetscObject)v, &name));
272   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
273 
274   PetscCall(VecGetLocalSize(coordinates, &N));
275   PetscCall(VecGetArrayRead(coordinates, &coords));
276   for (c = 0; c < N; c += dim) {
277     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));
278     bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
279     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c + 1]));
280     bound[3] = PetscMax(bound[3], PetscRealPart(coords[c + 1]));
281   }
282   PetscCall(VecRestoreArrayRead(coordinates, &coords));
283   PetscCall(PetscDrawClear(draw));
284 
285   /* Could implement something like DMDASelectFields() */
286   for (f = 0; f < Nf; ++f) {
287     DM          fdm = dm;
288     Vec         fv  = v;
289     IS          fis;
290     char        prefix[PETSC_MAX_PATH_LEN];
291     const char *fname;
292 
293     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
294     PetscCall(PetscSectionGetFieldName(s, f, &fname));
295 
296     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
297     else prefix[0] = '\0';
298     if (Nf > 1) {
299       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
300       PetscCall(VecGetSubVector(v, fis, &fv));
301       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
302       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
303     }
304     for (comp = 0; comp < Nc; ++comp, ++w) {
305       PetscInt nmax = 2;
306 
307       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
308       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
309       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
310       PetscCall(PetscDrawSetTitle(draw, title));
311 
312       /* TODO Get max and min only for this component */
313       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
314       if (!flg) {
315         PetscCall(VecMin(fv, NULL, &vbound[0]));
316         PetscCall(VecMax(fv, NULL, &vbound[1]));
317         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
318       }
319       PetscCall(PetscDrawGetPopup(draw, &popup));
320       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
321       PetscCall(PetscDrawSetCoordinates(draw, bound[0], bound[1], bound[2], bound[3]));
322 
323       PetscCall(VecGetArrayRead(fv, &array));
324       for (c = cStart; c < cEnd; ++c) {
325         PetscScalar *coords = NULL, *a   = NULL;
326         PetscInt     numCoords, color[4] = {-1, -1, -1, -1};
327 
328         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
329         if (a) {
330           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
331           color[1] = color[2] = color[3] = color[0];
332         } else {
333           PetscScalar *vals = NULL;
334           PetscInt     numVals, va;
335 
336           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
337           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);
338           switch (numVals / Nc) {
339           case 3: /* P1 Triangle */
340           case 4: /* P1 Quadrangle */
341             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
342             break;
343           case 6: /* P2 Triangle */
344           case 8: /* P2 Quadrangle */
345             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
346             break;
347           default:
348             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
349           }
350           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
351         }
352         PetscCall(DMPlexVecGetClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
353         switch (numCoords) {
354         case 6:
355         case 12: /* Localized triangle */
356           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]));
357           break;
358         case 8:
359         case 16: /* Localized quadrilateral */
360           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]));
361           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]));
362           break;
363         default:
364           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
365         }
366         PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
367       }
368       PetscCall(VecRestoreArrayRead(fv, &array));
369       PetscCall(PetscDrawFlush(draw));
370       PetscCall(PetscDrawPause(draw));
371       PetscCall(PetscDrawSave(draw));
372     }
373     if (Nf > 1) {
374       PetscCall(VecRestoreSubVector(v, fis, &fv));
375       PetscCall(ISDestroy(&fis));
376       PetscCall(DMDestroy(&fdm));
377     }
378   }
379   PetscFunctionReturn(0);
380 }
381 
382 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
383 {
384   DM        dm;
385   PetscDraw draw;
386   PetscInt  dim;
387   PetscBool isnull;
388 
389   PetscFunctionBegin;
390   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
391   PetscCall(PetscDrawIsNull(draw, &isnull));
392   if (isnull) PetscFunctionReturn(0);
393 
394   PetscCall(VecGetDM(v, &dm));
395   PetscCall(DMGetCoordinateDim(dm, &dim));
396   switch (dim) {
397   case 1:
398     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
399     break;
400   case 2:
401     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
402     break;
403   default:
404     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
405   }
406   PetscFunctionReturn(0);
407 }
408 
409 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
410 {
411   DM                      dm;
412   Vec                     locv;
413   const char             *name;
414   PetscSection            section;
415   PetscInt                pStart, pEnd;
416   PetscInt                numFields;
417   PetscViewerVTKFieldType ft;
418 
419   PetscFunctionBegin;
420   PetscCall(VecGetDM(v, &dm));
421   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
422   PetscCall(PetscObjectGetName((PetscObject)v, &name));
423   PetscCall(PetscObjectSetName((PetscObject)locv, name));
424   PetscCall(VecCopy(v, locv));
425   PetscCall(DMGetLocalSection(dm, &section));
426   PetscCall(PetscSectionGetNumFields(section, &numFields));
427   if (!numFields) {
428     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
429     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
430   } else {
431     PetscInt f;
432 
433     for (f = 0; f < numFields; f++) {
434       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
435       if (ft == PETSC_VTK_INVALID) continue;
436       PetscCall(PetscObjectReference((PetscObject)locv));
437       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
438     }
439     PetscCall(VecDestroy(&locv));
440   }
441   PetscFunctionReturn(0);
442 }
443 
444 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
445 {
446   DM        dm;
447   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
448 
449   PetscFunctionBegin;
450   PetscCall(VecGetDM(v, &dm));
451   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
452   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
453   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
454   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
455   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
456   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
457   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
458     PetscInt    i, numFields;
459     PetscObject fe;
460     PetscBool   fem  = PETSC_FALSE;
461     Vec         locv = v;
462     const char *name;
463     PetscInt    step;
464     PetscReal   time;
465 
466     PetscCall(DMGetNumFields(dm, &numFields));
467     for (i = 0; i < numFields; i++) {
468       PetscCall(DMGetField(dm, i, NULL, &fe));
469       if (fe->classid == PETSCFE_CLASSID) {
470         fem = PETSC_TRUE;
471         break;
472       }
473     }
474     if (fem) {
475       PetscObject isZero;
476 
477       PetscCall(DMGetLocalVector(dm, &locv));
478       PetscCall(PetscObjectGetName((PetscObject)v, &name));
479       PetscCall(PetscObjectSetName((PetscObject)locv, name));
480       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
481       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
482       PetscCall(VecCopy(v, locv));
483       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
484       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
485     }
486     if (isvtk) {
487       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
488     } else if (ishdf5) {
489 #if defined(PETSC_HAVE_HDF5)
490       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
491 #else
492       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
493 #endif
494     } else if (isdraw) {
495       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
496     } else if (isglvis) {
497       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
498       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
499       PetscCall(VecView_GLVis(locv, viewer));
500     } else if (iscgns) {
501 #if defined(PETSC_HAVE_CGNS)
502       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
503 #else
504       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
505 #endif
506     }
507     if (fem) {
508       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
509       PetscCall(DMRestoreLocalVector(dm, &locv));
510     }
511   } else {
512     PetscBool isseq;
513 
514     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
515     if (isseq) PetscCall(VecView_Seq(v, viewer));
516     else PetscCall(VecView_MPI(v, viewer));
517   }
518   PetscFunctionReturn(0);
519 }
520 
521 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
522 {
523   DM        dm;
524   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
525 
526   PetscFunctionBegin;
527   PetscCall(VecGetDM(v, &dm));
528   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
529   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
530   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
531   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
532   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
533   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
534   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
535   if (isvtk || isdraw || isglvis || iscgns) {
536     Vec         locv;
537     PetscObject isZero;
538     const char *name;
539 
540     PetscCall(DMGetLocalVector(dm, &locv));
541     PetscCall(PetscObjectGetName((PetscObject)v, &name));
542     PetscCall(PetscObjectSetName((PetscObject)locv, name));
543     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
544     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
545     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
546     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
547     PetscCall(VecView_Plex_Local(locv, viewer));
548     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
549     PetscCall(DMRestoreLocalVector(dm, &locv));
550   } else if (ishdf5) {
551 #if defined(PETSC_HAVE_HDF5)
552     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
553 #else
554     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
555 #endif
556   } else if (isexodusii) {
557 #if defined(PETSC_HAVE_EXODUSII)
558     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
559 #else
560     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
561 #endif
562   } else {
563     PetscBool isseq;
564 
565     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
566     if (isseq) PetscCall(VecView_Seq(v, viewer));
567     else PetscCall(VecView_MPI(v, viewer));
568   }
569   PetscFunctionReturn(0);
570 }
571 
572 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
573 {
574   DM                dm;
575   MPI_Comm          comm;
576   PetscViewerFormat format;
577   Vec               v;
578   PetscBool         isvtk, ishdf5;
579 
580   PetscFunctionBegin;
581   PetscCall(VecGetDM(originalv, &dm));
582   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
583   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
584   PetscCall(PetscViewerGetFormat(viewer, &format));
585   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
586   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
587   if (format == PETSC_VIEWER_NATIVE) {
588     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
589     /* this need a better fix */
590     if (dm->useNatural) {
591       if (dm->sfNatural) {
592         const char *vecname;
593         PetscInt    n, nroots;
594 
595         PetscCall(VecGetLocalSize(originalv, &n));
596         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
597         if (n == nroots) {
598           PetscCall(DMGetGlobalVector(dm, &v));
599           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
600           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
601           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
602           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
603         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
604       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
605     } else v = originalv;
606   } else v = originalv;
607 
608   if (ishdf5) {
609 #if defined(PETSC_HAVE_HDF5)
610     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
611 #else
612     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
613 #endif
614   } else if (isvtk) {
615     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
616   } else {
617     PetscBool isseq;
618 
619     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
620     if (isseq) PetscCall(VecView_Seq(v, viewer));
621     else PetscCall(VecView_MPI(v, viewer));
622   }
623   if (v != originalv) PetscCall(DMRestoreGlobalVector(dm, &v));
624   PetscFunctionReturn(0);
625 }
626 
627 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
628 {
629   DM        dm;
630   PetscBool ishdf5;
631 
632   PetscFunctionBegin;
633   PetscCall(VecGetDM(v, &dm));
634   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
635   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
636   if (ishdf5) {
637     DM          dmBC;
638     Vec         gv;
639     const char *name;
640 
641     PetscCall(DMGetOutputDM(dm, &dmBC));
642     PetscCall(DMGetGlobalVector(dmBC, &gv));
643     PetscCall(PetscObjectGetName((PetscObject)v, &name));
644     PetscCall(PetscObjectSetName((PetscObject)gv, name));
645     PetscCall(VecLoad_Default(gv, viewer));
646     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
647     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
648     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
649   } else PetscCall(VecLoad_Default(v, viewer));
650   PetscFunctionReturn(0);
651 }
652 
653 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
654 {
655   DM        dm;
656   PetscBool ishdf5, isexodusii;
657 
658   PetscFunctionBegin;
659   PetscCall(VecGetDM(v, &dm));
660   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
661   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
662   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
663   if (ishdf5) {
664 #if defined(PETSC_HAVE_HDF5)
665     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
666 #else
667     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
668 #endif
669   } else if (isexodusii) {
670 #if defined(PETSC_HAVE_EXODUSII)
671     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
672 #else
673     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
674 #endif
675   } else PetscCall(VecLoad_Default(v, viewer));
676   PetscFunctionReturn(0);
677 }
678 
679 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
680 {
681   DM                dm;
682   PetscViewerFormat format;
683   PetscBool         ishdf5;
684 
685   PetscFunctionBegin;
686   PetscCall(VecGetDM(originalv, &dm));
687   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
688   PetscCall(PetscViewerGetFormat(viewer, &format));
689   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
690   if (format == PETSC_VIEWER_NATIVE) {
691     if (dm->useNatural) {
692       if (dm->sfNatural) {
693         if (ishdf5) {
694 #if defined(PETSC_HAVE_HDF5)
695           Vec         v;
696           const char *vecname;
697 
698           PetscCall(DMGetGlobalVector(dm, &v));
699           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
700           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
701           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
702           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
703           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
704           PetscCall(DMRestoreGlobalVector(dm, &v));
705 #else
706           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
707 #endif
708         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
709       }
710     } else PetscCall(VecLoad_Default(originalv, viewer));
711   }
712   PetscFunctionReturn(0);
713 }
714 
715 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
716 {
717   PetscSection       coordSection;
718   Vec                coordinates;
719   DMLabel            depthLabel, celltypeLabel;
720   const char        *name[4];
721   const PetscScalar *a;
722   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
723 
724   PetscFunctionBegin;
725   PetscCall(DMGetDimension(dm, &dim));
726   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
727   PetscCall(DMGetCoordinateSection(dm, &coordSection));
728   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
729   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
730   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
731   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
732   PetscCall(VecGetArrayRead(coordinates, &a));
733   name[0]       = "vertex";
734   name[1]       = "edge";
735   name[dim - 1] = "face";
736   name[dim]     = "cell";
737   for (c = cStart; c < cEnd; ++c) {
738     PetscInt *closure = NULL;
739     PetscInt  closureSize, cl, ct;
740 
741     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
742     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
743     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
744     PetscCall(PetscViewerASCIIPushTab(viewer));
745     for (cl = 0; cl < closureSize * 2; cl += 2) {
746       PetscInt point = closure[cl], depth, dof, off, d, p;
747 
748       if ((point < pStart) || (point >= pEnd)) continue;
749       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
750       if (!dof) continue;
751       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
752       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
753       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
754       for (p = 0; p < dof / dim; ++p) {
755         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
756         for (d = 0; d < dim; ++d) {
757           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
758           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
759         }
760         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
761       }
762       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
763     }
764     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
765     PetscCall(PetscViewerASCIIPopTab(viewer));
766   }
767   PetscCall(VecRestoreArrayRead(coordinates, &a));
768   PetscFunctionReturn(0);
769 }
770 
771 typedef enum {
772   CS_CARTESIAN,
773   CS_POLAR,
774   CS_CYLINDRICAL,
775   CS_SPHERICAL
776 } CoordSystem;
777 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
778 
779 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
780 {
781   PetscInt i;
782 
783   PetscFunctionBegin;
784   if (dim > 3) {
785     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
786   } else {
787     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
788 
789     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
790     switch (cs) {
791     case CS_CARTESIAN:
792       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
793       break;
794     case CS_POLAR:
795       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
796       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
797       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
798       break;
799     case CS_CYLINDRICAL:
800       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
801       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
802       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
803       trcoords[2] = coords[2];
804       break;
805     case CS_SPHERICAL:
806       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
807       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
808       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
809       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
810       break;
811     }
812     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
813   }
814   PetscFunctionReturn(0);
815 }
816 
817 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
818 {
819   DM_Plex          *mesh = (DM_Plex *)dm->data;
820   DM                cdm, cdmCell;
821   PetscSection      coordSection, coordSectionCell;
822   Vec               coordinates, coordinatesCell;
823   PetscViewerFormat format;
824 
825   PetscFunctionBegin;
826   PetscCall(DMGetCoordinateDM(dm, &cdm));
827   PetscCall(DMGetCoordinateSection(dm, &coordSection));
828   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
829   PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
830   PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
831   PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
832   PetscCall(PetscViewerGetFormat(viewer, &format));
833   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
834     const char *name;
835     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
836     PetscInt    pStart, pEnd, p, numLabels, l;
837     PetscMPIInt rank, size;
838 
839     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
840     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
841     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
842     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
843     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
844     PetscCall(DMGetDimension(dm, &dim));
845     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
846     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
847     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
848     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
849     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
850     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
851     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
852     for (p = pStart; p < pEnd; ++p) {
853       PetscInt dof, off, s;
854 
855       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
856       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
857       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
858     }
859     PetscCall(PetscViewerFlush(viewer));
860     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
861     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
862     for (p = pStart; p < pEnd; ++p) {
863       PetscInt dof, off, c;
864 
865       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
866       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
867       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]));
868     }
869     PetscCall(PetscViewerFlush(viewer));
870     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
871     if (coordSection && coordinates) {
872       CoordSystem        cs = CS_CARTESIAN;
873       const PetscScalar *array, *arrayCell = NULL;
874       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
875       PetscMPIInt        rank;
876       const char        *name;
877 
878       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
879       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
880       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
881       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
882       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
883       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
884       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
885       pStart = PetscMin(pvStart, pcStart);
886       pEnd   = PetscMax(pvEnd, pcEnd);
887       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
888       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
889       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
890       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
891 
892       PetscCall(VecGetArrayRead(coordinates, &array));
893       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
894       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
895       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
896       for (p = pStart; p < pEnd; ++p) {
897         PetscInt dof, off;
898 
899         if (p >= pvStart && p < pvEnd) {
900           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
901           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
902           if (dof) {
903             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
904             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
905             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
906           }
907         }
908         if (cdmCell && p >= pcStart && p < pcEnd) {
909           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
910           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
911           if (dof) {
912             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
913             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
914             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
915           }
916         }
917       }
918       PetscCall(PetscViewerFlush(viewer));
919       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
920       PetscCall(VecRestoreArrayRead(coordinates, &array));
921       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
922     }
923     PetscCall(DMGetNumLabels(dm, &numLabels));
924     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
925     for (l = 0; l < numLabels; ++l) {
926       DMLabel     label;
927       PetscBool   isdepth;
928       const char *name;
929 
930       PetscCall(DMGetLabelName(dm, l, &name));
931       PetscCall(PetscStrcmp(name, "depth", &isdepth));
932       if (isdepth) continue;
933       PetscCall(DMGetLabel(dm, name, &label));
934       PetscCall(DMLabelView(label, viewer));
935     }
936     if (size > 1) {
937       PetscSF sf;
938 
939       PetscCall(DMGetPointSF(dm, &sf));
940       PetscCall(PetscSFView(sf, viewer));
941     }
942     PetscCall(PetscViewerFlush(viewer));
943   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
944     const char  *name, *color;
945     const char  *defcolors[3]  = {"gray", "orange", "green"};
946     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
947     char         lname[PETSC_MAX_PATH_LEN];
948     PetscReal    scale      = 2.0;
949     PetscReal    tikzscale  = 1.0;
950     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
951     double       tcoords[3];
952     PetscScalar *coords;
953     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
954     PetscMPIInt  rank, size;
955     char       **names, **colors, **lcolors;
956     PetscBool    flg, lflg;
957     PetscBT      wp = NULL;
958     PetscInt     pEnd, pStart;
959 
960     PetscCall(DMGetDimension(dm, &dim));
961     PetscCall(DMPlexGetDepth(dm, &depth));
962     PetscCall(DMGetNumLabels(dm, &numLabels));
963     numLabels  = PetscMax(numLabels, 10);
964     numColors  = 10;
965     numLColors = 10;
966     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
967     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
968     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
969     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
970     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
971     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
972     n = 4;
973     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
974     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
975     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
976     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
977     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
978     if (!useLabels) numLabels = 0;
979     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
980     if (!useColors) {
981       numColors = 3;
982       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
983     }
984     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
985     if (!useColors) {
986       numLColors = 4;
987       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
988     }
989     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
990     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
991     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
992     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
993     if (depth < dim) plotEdges = PETSC_FALSE;
994     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
995 
996     /* filter points with labelvalue != labeldefaultvalue */
997     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
998     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
999     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1000     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1001     if (lflg) {
1002       DMLabel lbl;
1003 
1004       PetscCall(DMGetLabel(dm, lname, &lbl));
1005       if (lbl) {
1006         PetscInt val, defval;
1007 
1008         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1009         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1010         for (c = pStart; c < pEnd; c++) {
1011           PetscInt *closure = NULL;
1012           PetscInt  closureSize;
1013 
1014           PetscCall(DMLabelGetValue(lbl, c, &val));
1015           if (val == defval) continue;
1016 
1017           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1018           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1019           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1020         }
1021       }
1022     }
1023 
1024     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1025     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1026     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1027     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1028 \\documentclass[tikz]{standalone}\n\n\
1029 \\usepackage{pgflibraryshapes}\n\
1030 \\usetikzlibrary{backgrounds}\n\
1031 \\usetikzlibrary{arrows}\n\
1032 \\begin{document}\n"));
1033     if (size > 1) {
1034       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1035       for (p = 0; p < size; ++p) {
1036         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1037         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1038       }
1039       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1040     }
1041     if (drawHasse) {
1042       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, cEnd - cStart));
1043 
1044       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1045       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1046       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1047       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1048       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1049       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1050       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1051       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1052       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1053       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1054       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1055       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1056     }
1057     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1058 
1059     /* Plot vertices */
1060     PetscCall(VecGetArray(coordinates, &coords));
1061     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1062     for (v = vStart; v < vEnd; ++v) {
1063       PetscInt  off, dof, d;
1064       PetscBool isLabeled = PETSC_FALSE;
1065 
1066       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1067       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1068       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1069       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1070       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1071       for (d = 0; d < dof; ++d) {
1072         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1073         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1074       }
1075       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1076       if (dim == 3) {
1077         PetscReal tmp = tcoords[1];
1078         tcoords[1]    = tcoords[2];
1079         tcoords[2]    = -tmp;
1080       }
1081       for (d = 0; d < dof; ++d) {
1082         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1083         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1084       }
1085       if (drawHasse) color = colors[0 % numColors];
1086       else color = colors[rank % numColors];
1087       for (l = 0; l < numLabels; ++l) {
1088         PetscInt val;
1089         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1090         if (val >= 0) {
1091           color     = lcolors[l % numLColors];
1092           isLabeled = PETSC_TRUE;
1093           break;
1094         }
1095       }
1096       if (drawNumbers[0]) {
1097         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1098       } else if (drawColors[0]) {
1099         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1100       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1101     }
1102     PetscCall(VecRestoreArray(coordinates, &coords));
1103     PetscCall(PetscViewerFlush(viewer));
1104     /* Plot edges */
1105     if (plotEdges) {
1106       PetscCall(VecGetArray(coordinates, &coords));
1107       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1108       for (e = eStart; e < eEnd; ++e) {
1109         const PetscInt *cone;
1110         PetscInt        coneSize, offA, offB, dof, d;
1111 
1112         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1113         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1114         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1115         PetscCall(DMPlexGetCone(dm, e, &cone));
1116         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1117         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1118         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1119         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1120         for (d = 0; d < dof; ++d) {
1121           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1122           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1123         }
1124         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1125         if (dim == 3) {
1126           PetscReal tmp = tcoords[1];
1127           tcoords[1]    = tcoords[2];
1128           tcoords[2]    = -tmp;
1129         }
1130         for (d = 0; d < dof; ++d) {
1131           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1132           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1133         }
1134         if (drawHasse) color = colors[1 % numColors];
1135         else color = colors[rank % numColors];
1136         for (l = 0; l < numLabels; ++l) {
1137           PetscInt val;
1138           PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1139           if (val >= 0) {
1140             color = lcolors[l % numLColors];
1141             break;
1142           }
1143         }
1144         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1145       }
1146       PetscCall(VecRestoreArray(coordinates, &coords));
1147       PetscCall(PetscViewerFlush(viewer));
1148       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1149     }
1150     /* Plot cells */
1151     if (dim == 3 || !drawNumbers[1]) {
1152       for (e = eStart; e < eEnd; ++e) {
1153         const PetscInt *cone;
1154 
1155         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1156         color = colors[rank % numColors];
1157         for (l = 0; l < numLabels; ++l) {
1158           PetscInt val;
1159           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1160           if (val >= 0) {
1161             color = lcolors[l % numLColors];
1162             break;
1163           }
1164         }
1165         PetscCall(DMPlexGetCone(dm, e, &cone));
1166         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1167       }
1168     } else {
1169       DMPolytopeType ct;
1170 
1171       /* Drawing a 2D polygon */
1172       for (c = cStart; c < cEnd; ++c) {
1173         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1174         PetscCall(DMPlexGetCellType(dm, c, &ct));
1175         if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
1176           const PetscInt *cone;
1177           PetscInt        coneSize, e;
1178 
1179           PetscCall(DMPlexGetCone(dm, c, &cone));
1180           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1181           for (e = 0; e < coneSize; ++e) {
1182             const PetscInt *econe;
1183 
1184             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1185             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));
1186           }
1187         } else {
1188           PetscInt *closure = NULL;
1189           PetscInt  closureSize, Nv = 0, v;
1190 
1191           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1192           for (p = 0; p < closureSize * 2; p += 2) {
1193             const PetscInt point = closure[p];
1194 
1195             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1196           }
1197           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1198           for (v = 0; v <= Nv; ++v) {
1199             const PetscInt vertex = closure[v % Nv];
1200 
1201             if (v > 0) {
1202               if (plotEdges) {
1203                 const PetscInt *edge;
1204                 PetscInt        endpoints[2], ne;
1205 
1206                 endpoints[0] = closure[v - 1];
1207                 endpoints[1] = vertex;
1208                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1209                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1210                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1211                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1212               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1213             }
1214             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1215           }
1216           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1217           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1218         }
1219       }
1220     }
1221     for (c = cStart; c < cEnd; ++c) {
1222       double             ccoords[3] = {0.0, 0.0, 0.0};
1223       PetscBool          isLabeled  = PETSC_FALSE;
1224       PetscScalar       *cellCoords = NULL;
1225       const PetscScalar *array;
1226       PetscInt           numCoords, cdim, d;
1227       PetscBool          isDG;
1228 
1229       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1230       PetscCall(DMGetCoordinateDim(dm, &cdim));
1231       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1232       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1233       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1234       for (p = 0; p < numCoords / cdim; ++p) {
1235         for (d = 0; d < cdim; ++d) {
1236           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1237           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1238         }
1239         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1240         if (cdim == 3) {
1241           PetscReal tmp = tcoords[1];
1242           tcoords[1]    = tcoords[2];
1243           tcoords[2]    = -tmp;
1244         }
1245         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1246       }
1247       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1248       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1249       for (d = 0; d < cdim; ++d) {
1250         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1251         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1252       }
1253       if (drawHasse) color = colors[depth % numColors];
1254       else color = colors[rank % numColors];
1255       for (l = 0; l < numLabels; ++l) {
1256         PetscInt val;
1257         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1258         if (val >= 0) {
1259           color     = lcolors[l % numLColors];
1260           isLabeled = PETSC_TRUE;
1261           break;
1262         }
1263       }
1264       if (drawNumbers[dim]) {
1265         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1266       } else if (drawColors[dim]) {
1267         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1268       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1269     }
1270     if (drawHasse) {
1271       color = colors[depth % numColors];
1272       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1273       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1274       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1275       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1276       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1277 
1278       color = colors[1 % numColors];
1279       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1280       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1281       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1282       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1283       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1284 
1285       color = colors[0 % numColors];
1286       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1287       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1288       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1289       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1290       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1291 
1292       for (p = pStart; p < pEnd; ++p) {
1293         const PetscInt *cone;
1294         PetscInt        coneSize, cp;
1295 
1296         PetscCall(DMPlexGetCone(dm, p, &cone));
1297         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1298         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1299       }
1300     }
1301     PetscCall(PetscViewerFlush(viewer));
1302     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1303     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1304     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1305     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1306     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1307     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1308     PetscCall(PetscFree3(names, colors, lcolors));
1309     PetscCall(PetscBTDestroy(&wp));
1310   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1311     Vec                    cown, acown;
1312     VecScatter             sct;
1313     ISLocalToGlobalMapping g2l;
1314     IS                     gid, acis;
1315     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1316     MPI_Group              ggroup, ngroup;
1317     PetscScalar           *array, nid;
1318     const PetscInt        *idxs;
1319     PetscInt              *idxs2, *start, *adjacency, *work;
1320     PetscInt64             lm[3], gm[3];
1321     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1322     PetscMPIInt            d1, d2, rank;
1323 
1324     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1325     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1326 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1327     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1328 #endif
1329     if (ncomm != MPI_COMM_NULL) {
1330       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1331       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1332       d1 = 0;
1333       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1334       nid = d2;
1335       PetscCallMPI(MPI_Group_free(&ggroup));
1336       PetscCallMPI(MPI_Group_free(&ngroup));
1337       PetscCallMPI(MPI_Comm_free(&ncomm));
1338     } else nid = 0.0;
1339 
1340     /* Get connectivity */
1341     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1342     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1343 
1344     /* filter overlapped local cells */
1345     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1346     PetscCall(ISGetIndices(gid, &idxs));
1347     PetscCall(ISGetLocalSize(gid, &cum));
1348     PetscCall(PetscMalloc1(cum, &idxs2));
1349     for (c = cStart, cum = 0; c < cEnd; c++) {
1350       if (idxs[c - cStart] < 0) continue;
1351       idxs2[cum++] = idxs[c - cStart];
1352     }
1353     PetscCall(ISRestoreIndices(gid, &idxs));
1354     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1355     PetscCall(ISDestroy(&gid));
1356     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1357 
1358     /* support for node-aware cell locality */
1359     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1360     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1361     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1362     PetscCall(VecGetArray(cown, &array));
1363     for (c = 0; c < numVertices; c++) array[c] = nid;
1364     PetscCall(VecRestoreArray(cown, &array));
1365     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1366     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1367     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1368     PetscCall(ISDestroy(&acis));
1369     PetscCall(VecScatterDestroy(&sct));
1370     PetscCall(VecDestroy(&cown));
1371 
1372     /* compute edgeCut */
1373     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1374     PetscCall(PetscMalloc1(cum, &work));
1375     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1376     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1377     PetscCall(ISDestroy(&gid));
1378     PetscCall(VecGetArray(acown, &array));
1379     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1380       PetscInt totl;
1381 
1382       totl = start[c + 1] - start[c];
1383       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1384       for (i = 0; i < totl; i++) {
1385         if (work[i] < 0) {
1386           ect += 1;
1387           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1388         }
1389       }
1390     }
1391     PetscCall(PetscFree(work));
1392     PetscCall(VecRestoreArray(acown, &array));
1393     lm[0] = numVertices > 0 ? numVertices : PETSC_MAX_INT;
1394     lm[1] = -numVertices;
1395     PetscCall(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1396     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1397     lm[0] = ect;                     /* edgeCut */
1398     lm[1] = ectn;                    /* node-aware edgeCut */
1399     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1400     PetscCall(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1401     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1402 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1403     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)(gm[1])) / ((double)gm[0]) : 1.));
1404 #else
1405     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1406 #endif
1407     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1408     PetscCall(PetscFree(start));
1409     PetscCall(PetscFree(adjacency));
1410     PetscCall(VecDestroy(&acown));
1411   } else {
1412     const char    *name;
1413     PetscInt      *sizes, *hybsizes, *ghostsizes;
1414     PetscInt       locDepth, depth, cellHeight, dim, d;
1415     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1416     PetscInt       numLabels, l, maxSize = 17;
1417     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1418     MPI_Comm       comm;
1419     PetscMPIInt    size, rank;
1420 
1421     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1422     PetscCallMPI(MPI_Comm_size(comm, &size));
1423     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1424     PetscCall(DMGetDimension(dm, &dim));
1425     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1426     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1427     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1428     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1429     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1430     PetscCall(DMPlexGetDepth(dm, &locDepth));
1431     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1432     PetscCall(DMPlexGetGhostCellStratum(dm, &gcStart, &gcEnd));
1433     gcNum = gcEnd - gcStart;
1434     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1435     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1436     for (d = 0; d <= depth; d++) {
1437       PetscInt Nc[2] = {0, 0}, ict;
1438 
1439       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1440       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1441       ict = ct0;
1442       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1443       ct0 = (DMPolytopeType)ict;
1444       for (p = pStart; p < pEnd; ++p) {
1445         DMPolytopeType ct;
1446 
1447         PetscCall(DMPlexGetCellType(dm, p, &ct));
1448         if (ct == ct0) ++Nc[0];
1449         else ++Nc[1];
1450       }
1451       if (size < maxSize) {
1452         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1453         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1454         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1455         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1456         for (p = 0; p < size; ++p) {
1457           if (rank == 0) {
1458             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1459             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1460             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1461           }
1462         }
1463       } else {
1464         PetscInt locMinMax[2];
1465 
1466         locMinMax[0] = Nc[0] + Nc[1];
1467         locMinMax[1] = Nc[0] + Nc[1];
1468         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1469         locMinMax[0] = Nc[1];
1470         locMinMax[1] = Nc[1];
1471         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1472         if (d == depth) {
1473           locMinMax[0] = gcNum;
1474           locMinMax[1] = gcNum;
1475           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1476         }
1477         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1478         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1479         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1480         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1481       }
1482       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1483     }
1484     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1485     {
1486       const PetscReal *maxCell;
1487       const PetscReal *L;
1488       PetscBool        localized;
1489 
1490       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1491       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1492       if (L || localized) {
1493         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1494         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1495         if (L) {
1496           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1497           for (d = 0; d < dim; ++d) {
1498             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1499             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1500           }
1501           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1502         }
1503         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1504         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1505       }
1506     }
1507     PetscCall(DMGetNumLabels(dm, &numLabels));
1508     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1509     for (l = 0; l < numLabels; ++l) {
1510       DMLabel         label;
1511       const char     *name;
1512       IS              valueIS;
1513       const PetscInt *values;
1514       PetscInt        numValues, v;
1515 
1516       PetscCall(DMGetLabelName(dm, l, &name));
1517       PetscCall(DMGetLabel(dm, name, &label));
1518       PetscCall(DMLabelGetNumValues(label, &numValues));
1519       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1520       PetscCall(DMLabelGetValueIS(label, &valueIS));
1521       PetscCall(ISGetIndices(valueIS, &values));
1522       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1523       for (v = 0; v < numValues; ++v) {
1524         PetscInt size;
1525 
1526         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1527         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1528         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1529       }
1530       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1531       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1532       PetscCall(ISRestoreIndices(valueIS, &values));
1533       PetscCall(ISDestroy(&valueIS));
1534     }
1535     {
1536       char    **labelNames;
1537       PetscInt  Nl = numLabels;
1538       PetscBool flg;
1539 
1540       PetscCall(PetscMalloc1(Nl, &labelNames));
1541       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1542       for (l = 0; l < Nl; ++l) {
1543         DMLabel label;
1544 
1545         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1546         if (flg) {
1547           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1548           PetscCall(DMLabelView(label, viewer));
1549         }
1550         PetscCall(PetscFree(labelNames[l]));
1551       }
1552       PetscCall(PetscFree(labelNames));
1553     }
1554     /* If no fields are specified, people do not want to see adjacency */
1555     if (dm->Nf) {
1556       PetscInt f;
1557 
1558       for (f = 0; f < dm->Nf; ++f) {
1559         const char *name;
1560 
1561         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1562         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1563         PetscCall(PetscViewerASCIIPushTab(viewer));
1564         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1565         if (dm->fields[f].adjacency[0]) {
1566           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1567           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1568         } else {
1569           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1570           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1571         }
1572         PetscCall(PetscViewerASCIIPopTab(viewer));
1573       }
1574     }
1575     PetscCall(DMGetCoarseDM(dm, &cdm));
1576     if (cdm) {
1577       PetscCall(PetscViewerASCIIPushTab(viewer));
1578       PetscCall(DMPlexView_Ascii(cdm, viewer));
1579       PetscCall(PetscViewerASCIIPopTab(viewer));
1580     }
1581   }
1582   PetscFunctionReturn(0);
1583 }
1584 
1585 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1586 {
1587   DMPolytopeType ct;
1588   PetscMPIInt    rank;
1589   PetscInt       cdim;
1590 
1591   PetscFunctionBegin;
1592   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1593   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1594   PetscCall(DMGetCoordinateDim(dm, &cdim));
1595   switch (ct) {
1596   case DM_POLYTOPE_SEGMENT:
1597   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1598     switch (cdim) {
1599     case 1: {
1600       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1601       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1602 
1603       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1604       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1605       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1606     } break;
1607     case 2: {
1608       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1609       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1610       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1611 
1612       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1613       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]) + l * dx, PetscRealPart(coords[1]) + l * dy, PetscRealPart(coords[0]) - l * dx, PetscRealPart(coords[1]) - l * dy, PETSC_DRAW_BLACK));
1614       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]) + l * dx, PetscRealPart(coords[3]) + l * dy, PetscRealPart(coords[2]) - l * dx, PetscRealPart(coords[3]) - l * dy, PETSC_DRAW_BLACK));
1615     } break;
1616     default:
1617       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1618     }
1619     break;
1620   case DM_POLYTOPE_TRIANGLE:
1621     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1622     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1623     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1624     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1625     break;
1626   case DM_POLYTOPE_QUADRILATERAL:
1627     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1628     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1629     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1630     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1631     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1632     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1633     break;
1634   case DM_POLYTOPE_FV_GHOST:
1635     break;
1636   default:
1637     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1638   }
1639   PetscFunctionReturn(0);
1640 }
1641 
1642 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1643 {
1644   DMPolytopeType ct;
1645   PetscReal      centroid[2] = {0., 0.};
1646   PetscMPIInt    rank;
1647   PetscInt       fillColor, v, e, d;
1648 
1649   PetscFunctionBegin;
1650   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1651   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1652   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1653   switch (ct) {
1654   case DM_POLYTOPE_TRIANGLE: {
1655     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1656 
1657     for (v = 0; v < 3; ++v) {
1658       centroid[0] += PetscRealPart(coords[v * 2 + 0]) / 3.;
1659       centroid[1] += PetscRealPart(coords[v * 2 + 1]) / 3.;
1660     }
1661     for (e = 0; e < 3; ++e) {
1662       refCoords[0] = refVertices[e * 2 + 0];
1663       refCoords[1] = refVertices[e * 2 + 1];
1664       for (d = 1; d <= edgeDiv; ++d) {
1665         refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % 3 * 2 + 0] - refCoords[0]) * d / edgeDiv;
1666         refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % 3 * 2 + 1] - refCoords[1]) * d / edgeDiv;
1667       }
1668       PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1669       for (d = 0; d < edgeDiv; ++d) {
1670         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));
1671         PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1672       }
1673     }
1674   } break;
1675   default:
1676     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1677   }
1678   PetscFunctionReturn(0);
1679 }
1680 
1681 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1682 {
1683   PetscDraw          draw;
1684   DM                 cdm;
1685   PetscSection       coordSection;
1686   Vec                coordinates;
1687   const PetscScalar *coords;
1688   PetscReal          xyl[2], xyr[2], bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
1689   PetscReal         *refCoords, *edgeCoords;
1690   PetscBool          isnull, drawAffine = PETSC_TRUE;
1691   PetscInt           dim, vStart, vEnd, cStart, cEnd, c, N, edgeDiv = 4;
1692 
1693   PetscFunctionBegin;
1694   PetscCall(DMGetCoordinateDim(dm, &dim));
1695   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1696   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1697   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1698   PetscCall(DMGetCoordinateDM(dm, &cdm));
1699   PetscCall(DMGetLocalSection(cdm, &coordSection));
1700   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1701   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1702   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1703 
1704   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1705   PetscCall(PetscDrawIsNull(draw, &isnull));
1706   if (isnull) PetscFunctionReturn(0);
1707   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1708 
1709   PetscCall(VecGetLocalSize(coordinates, &N));
1710   PetscCall(VecGetArrayRead(coordinates, &coords));
1711   for (c = 0; c < N; c += dim) {
1712     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));
1713     bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
1714     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c + 1]));
1715     bound[3] = PetscMax(bound[3], PetscRealPart(coords[c + 1]));
1716   }
1717   PetscCall(VecRestoreArrayRead(coordinates, &coords));
1718   PetscCall(MPIU_Allreduce(&bound[0], xyl, 2, MPIU_REAL, MPIU_MIN, PetscObjectComm((PetscObject)dm)));
1719   PetscCall(MPIU_Allreduce(&bound[2], xyr, 2, MPIU_REAL, MPIU_MAX, PetscObjectComm((PetscObject)dm)));
1720   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1721   PetscCall(PetscDrawClear(draw));
1722 
1723   for (c = cStart; c < cEnd; ++c) {
1724     PetscScalar *coords = NULL;
1725     PetscInt     numCoords;
1726 
1727     PetscCall(DMPlexVecGetClosureAtDepth_Internal(dm, coordSection, coordinates, c, 0, &numCoords, &coords));
1728     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1729     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1730     PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
1731   }
1732   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1733   PetscCall(PetscDrawFlush(draw));
1734   PetscCall(PetscDrawPause(draw));
1735   PetscCall(PetscDrawSave(draw));
1736   PetscFunctionReturn(0);
1737 }
1738 
1739 #if defined(PETSC_HAVE_EXODUSII)
1740   #include <exodusII.h>
1741   #include <petscviewerexodusii.h>
1742 #endif
1743 
1744 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1745 {
1746   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1747   char      name[PETSC_MAX_PATH_LEN];
1748 
1749   PetscFunctionBegin;
1750   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1751   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1752   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1753   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1754   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1755   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1756   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1757   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1758   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1759   if (iascii) {
1760     PetscViewerFormat format;
1761     PetscCall(PetscViewerGetFormat(viewer, &format));
1762     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1763     else PetscCall(DMPlexView_Ascii(dm, viewer));
1764   } else if (ishdf5) {
1765 #if defined(PETSC_HAVE_HDF5)
1766     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1767 #else
1768     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1769 #endif
1770   } else if (isvtk) {
1771     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1772   } else if (isdraw) {
1773     PetscCall(DMPlexView_Draw(dm, viewer));
1774   } else if (isglvis) {
1775     PetscCall(DMPlexView_GLVis(dm, viewer));
1776 #if defined(PETSC_HAVE_EXODUSII)
1777   } else if (isexodus) {
1778     /*
1779       exodusII requires that all sets be part of exactly one cell set.
1780       If the dm does not have a "Cell Sets" label defined, we create one
1781       with ID 1, containig all cells.
1782       Note that if the Cell Sets label is defined but does not cover all cells,
1783       we may still have a problem. This should probably be checked here or in the viewer;
1784     */
1785     PetscInt numCS;
1786     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
1787     if (!numCS) {
1788       PetscInt cStart, cEnd, c;
1789       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1790       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1791       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1792     }
1793     PetscCall(DMView_PlexExodusII(dm, viewer));
1794 #endif
1795 #if defined(PETSC_HAVE_CGNS)
1796   } else if (iscgns) {
1797     PetscCall(DMView_PlexCGNS(dm, viewer));
1798 #endif
1799   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1800   /* Optionally view the partition */
1801   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
1802   if (flg) {
1803     Vec ranks;
1804     PetscCall(DMPlexCreateRankField(dm, &ranks));
1805     PetscCall(VecView(ranks, viewer));
1806     PetscCall(VecDestroy(&ranks));
1807   }
1808   /* Optionally view a label */
1809   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1810   if (flg) {
1811     DMLabel label;
1812     Vec     val;
1813 
1814     PetscCall(DMGetLabel(dm, name, &label));
1815     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1816     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1817     PetscCall(VecView(val, viewer));
1818     PetscCall(VecDestroy(&val));
1819   }
1820   PetscFunctionReturn(0);
1821 }
1822 
1823 /*@
1824   DMPlexTopologyView - Saves a DMPlex topology into a file
1825 
1826   Collective on DM
1827 
1828   Input Parameters:
1829 + dm                - The DM whose topology is to be saved
1830 - viewer            - The PetscViewer for saving
1831 
1832   Level: advanced
1833 
1834 .seealso: `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`
1835 @*/
1836 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
1837 {
1838   PetscBool ishdf5;
1839 
1840   PetscFunctionBegin;
1841   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1842   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1843   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1844   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
1845   if (ishdf5) {
1846 #if defined(PETSC_HAVE_HDF5)
1847     PetscViewerFormat format;
1848     PetscCall(PetscViewerGetFormat(viewer, &format));
1849     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1850       IS globalPointNumbering;
1851 
1852       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1853       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
1854       PetscCall(ISDestroy(&globalPointNumbering));
1855     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1856 #else
1857     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1858 #endif
1859   }
1860   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
1861   PetscFunctionReturn(0);
1862 }
1863 
1864 /*@
1865   DMPlexCoordinatesView - Saves DMPlex coordinates into a file
1866 
1867   Collective on DM
1868 
1869   Input Parameters:
1870 + dm     - The DM whose coordinates are to be saved
1871 - viewer - The PetscViewer for saving
1872 
1873   Level: advanced
1874 
1875 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`
1876 @*/
1877 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
1878 {
1879   PetscBool ishdf5;
1880 
1881   PetscFunctionBegin;
1882   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1883   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1884   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1885   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1886   if (ishdf5) {
1887 #if defined(PETSC_HAVE_HDF5)
1888     PetscViewerFormat format;
1889     PetscCall(PetscViewerGetFormat(viewer, &format));
1890     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1891       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
1892     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1893 #else
1894     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1895 #endif
1896   }
1897   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1898   PetscFunctionReturn(0);
1899 }
1900 
1901 /*@
1902   DMPlexLabelsView - Saves DMPlex labels into a file
1903 
1904   Collective on DM
1905 
1906   Input Parameters:
1907 + dm     - The DM whose labels are to be saved
1908 - viewer - The PetscViewer for saving
1909 
1910   Level: advanced
1911 
1912 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`
1913 @*/
1914 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
1915 {
1916   PetscBool ishdf5;
1917 
1918   PetscFunctionBegin;
1919   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1920   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1921   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1922   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
1923   if (ishdf5) {
1924 #if defined(PETSC_HAVE_HDF5)
1925     IS                globalPointNumbering;
1926     PetscViewerFormat format;
1927 
1928     PetscCall(PetscViewerGetFormat(viewer, &format));
1929     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1930       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1931       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
1932       PetscCall(ISDestroy(&globalPointNumbering));
1933     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1934 #else
1935     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1936 #endif
1937   }
1938   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
1939   PetscFunctionReturn(0);
1940 }
1941 
1942 /*@
1943   DMPlexSectionView - Saves a section associated with a DMPlex
1944 
1945   Collective on DM
1946 
1947   Input Parameters:
1948 + dm         - The DM that contains the topology on which the section to be saved is defined
1949 . viewer     - The PetscViewer for saving
1950 - sectiondm  - The DM that contains the section to be saved
1951 
1952   Level: advanced
1953 
1954   Notes:
1955   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.
1956 
1957   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 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.
1958 
1959 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`
1960 @*/
1961 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
1962 {
1963   PetscBool ishdf5;
1964 
1965   PetscFunctionBegin;
1966   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1967   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1968   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1969   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1970   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
1971   if (ishdf5) {
1972 #if defined(PETSC_HAVE_HDF5)
1973     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
1974 #else
1975     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1976 #endif
1977   }
1978   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
1979   PetscFunctionReturn(0);
1980 }
1981 
1982 /*@
1983   DMPlexGlobalVectorView - Saves a global vector
1984 
1985   Collective on DM
1986 
1987   Input Parameters:
1988 + dm        - The DM that represents the topology
1989 . viewer    - The PetscViewer to save data with
1990 . sectiondm - The DM that contains the global section on which vec is defined
1991 - vec       - The global vector to be saved
1992 
1993   Level: advanced
1994 
1995   Notes:
1996   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 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.
1997 
1998   Typical calling sequence
1999 $       DMCreate(PETSC_COMM_WORLD, &dm);
2000 $       DMSetType(dm, DMPLEX);
2001 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2002 $       DMClone(dm, &sectiondm);
2003 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2004 $       PetscSectionCreate(PETSC_COMM_WORLD, &section);
2005 $       DMPlexGetChart(sectiondm, &pStart, &pEnd);
2006 $       PetscSectionSetChart(section, pStart, pEnd);
2007 $       PetscSectionSetUp(section);
2008 $       DMSetLocalSection(sectiondm, section);
2009 $       PetscSectionDestroy(&section);
2010 $       DMGetGlobalVector(sectiondm, &vec);
2011 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2012 $       DMPlexTopologyView(dm, viewer);
2013 $       DMPlexSectionView(dm, viewer, sectiondm);
2014 $       DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2015 $       DMRestoreGlobalVector(sectiondm, &vec);
2016 $       DMDestroy(&sectiondm);
2017 $       DMDestroy(&dm);
2018 
2019 .seealso: `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2020 @*/
2021 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2022 {
2023   PetscBool ishdf5;
2024 
2025   PetscFunctionBegin;
2026   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2027   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2028   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2029   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2030   /* Check consistency */
2031   {
2032     PetscSection section;
2033     PetscBool    includesConstraints;
2034     PetscInt     m, m1;
2035 
2036     PetscCall(VecGetLocalSize(vec, &m1));
2037     PetscCall(DMGetGlobalSection(sectiondm, &section));
2038     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2039     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2040     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2041     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2042   }
2043   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2044   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2045   if (ishdf5) {
2046 #if defined(PETSC_HAVE_HDF5)
2047     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2048 #else
2049     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2050 #endif
2051   }
2052   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2053   PetscFunctionReturn(0);
2054 }
2055 
2056 /*@
2057   DMPlexLocalVectorView - Saves a local vector
2058 
2059   Collective on DM
2060 
2061   Input Parameters:
2062 + dm        - The DM that represents the topology
2063 . viewer    - The PetscViewer to save data with
2064 . sectiondm - The DM that contains the local section on which vec is defined; may be the same as dm
2065 - vec       - The local vector to be saved
2066 
2067   Level: advanced
2068 
2069   Notes:
2070   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 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.
2071 
2072   Typical calling sequence
2073 $       DMCreate(PETSC_COMM_WORLD, &dm);
2074 $       DMSetType(dm, DMPLEX);
2075 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2076 $       DMClone(dm, &sectiondm);
2077 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2078 $       PetscSectionCreate(PETSC_COMM_WORLD, &section);
2079 $       DMPlexGetChart(sectiondm, &pStart, &pEnd);
2080 $       PetscSectionSetChart(section, pStart, pEnd);
2081 $       PetscSectionSetUp(section);
2082 $       DMSetLocalSection(sectiondm, section);
2083 $       DMGetLocalVector(sectiondm, &vec);
2084 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2085 $       DMPlexTopologyView(dm, viewer);
2086 $       DMPlexSectionView(dm, viewer, sectiondm);
2087 $       DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2088 $       DMRestoreLocalVector(sectiondm, &vec);
2089 $       DMDestroy(&sectiondm);
2090 $       DMDestroy(&dm);
2091 
2092 .seealso: `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2093 @*/
2094 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2095 {
2096   PetscBool ishdf5;
2097 
2098   PetscFunctionBegin;
2099   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2100   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2101   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2102   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2103   /* Check consistency */
2104   {
2105     PetscSection section;
2106     PetscBool    includesConstraints;
2107     PetscInt     m, m1;
2108 
2109     PetscCall(VecGetLocalSize(vec, &m1));
2110     PetscCall(DMGetLocalSection(sectiondm, &section));
2111     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2112     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2113     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2114     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2115   }
2116   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2117   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2118   if (ishdf5) {
2119 #if defined(PETSC_HAVE_HDF5)
2120     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2121 #else
2122     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2123 #endif
2124   }
2125   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2126   PetscFunctionReturn(0);
2127 }
2128 
2129 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2130 {
2131   PetscBool ishdf5;
2132 
2133   PetscFunctionBegin;
2134   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2135   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2136   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2137   if (ishdf5) {
2138 #if defined(PETSC_HAVE_HDF5)
2139     PetscViewerFormat format;
2140     PetscCall(PetscViewerGetFormat(viewer, &format));
2141     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2142       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2143     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2144       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2145     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2146     PetscFunctionReturn(0);
2147 #else
2148     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2149 #endif
2150   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2151 }
2152 
2153 /*@
2154   DMPlexTopologyLoad - Loads a topology into a DMPlex
2155 
2156   Collective on DM
2157 
2158   Input Parameters:
2159 + dm                - The DM into which the topology is loaded
2160 - viewer            - The PetscViewer for the saved topology
2161 
2162   Output Parameters:
2163 . globalToLocalPointSF - The PetscSF that pushes points in [0, N) to the associated points in the loaded plex, where N is the global number of points; NULL if unneeded
2164 
2165   Level: advanced
2166 
2167 .seealso: `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2168 @*/
2169 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2170 {
2171   PetscBool ishdf5;
2172 
2173   PetscFunctionBegin;
2174   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2175   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2176   if (globalToLocalPointSF) PetscValidPointer(globalToLocalPointSF, 3);
2177   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2178   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2179   if (ishdf5) {
2180 #if defined(PETSC_HAVE_HDF5)
2181     PetscViewerFormat format;
2182     PetscCall(PetscViewerGetFormat(viewer, &format));
2183     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2184       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2185     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2186 #else
2187     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2188 #endif
2189   }
2190   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2191   PetscFunctionReturn(0);
2192 }
2193 
2194 /*@
2195   DMPlexCoordinatesLoad - Loads coordinates into a DMPlex
2196 
2197   Collective on DM
2198 
2199   Input Parameters:
2200 + dm     - The DM into which the coordinates are loaded
2201 . viewer - The PetscViewer for the saved coordinates
2202 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2203 
2204   Level: advanced
2205 
2206 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2207 @*/
2208 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2209 {
2210   PetscBool ishdf5;
2211 
2212   PetscFunctionBegin;
2213   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2214   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2215   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2216   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2217   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2218   if (ishdf5) {
2219 #if defined(PETSC_HAVE_HDF5)
2220     PetscViewerFormat format;
2221     PetscCall(PetscViewerGetFormat(viewer, &format));
2222     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2223       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2224     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2225 #else
2226     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2227 #endif
2228   }
2229   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2230   PetscFunctionReturn(0);
2231 }
2232 
2233 /*@
2234   DMPlexLabelsLoad - Loads labels into a DMPlex
2235 
2236   Collective on DM
2237 
2238   Input Parameters:
2239 + dm     - The DM into which the labels are loaded
2240 . viewer - The PetscViewer for the saved labels
2241 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2242 
2243   Level: advanced
2244 
2245   Notes:
2246   The PetscSF argument must not be NULL if the DM is distributed, otherwise an error occurs.
2247 
2248 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2249 @*/
2250 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2251 {
2252   PetscBool ishdf5;
2253 
2254   PetscFunctionBegin;
2255   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2256   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2257   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2258   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2259   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2260   if (ishdf5) {
2261 #if defined(PETSC_HAVE_HDF5)
2262     PetscViewerFormat format;
2263 
2264     PetscCall(PetscViewerGetFormat(viewer, &format));
2265     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2266       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2267     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2268 #else
2269     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2270 #endif
2271   }
2272   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2273   PetscFunctionReturn(0);
2274 }
2275 
2276 /*@
2277   DMPlexSectionLoad - Loads section into a DMPlex
2278 
2279   Collective on DM
2280 
2281   Input Parameters:
2282 + dm          - The DM that represents the topology
2283 . viewer      - The PetscViewer that represents the on-disk section (sectionA)
2284 . sectiondm   - The DM into which the on-disk section (sectionA) is migrated
2285 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2286 
2287   Output Parameters
2288 + globalDofSF - The SF 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)
2289 - localDofSF  - The SF 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)
2290 
2291   Level: advanced
2292 
2293   Notes:
2294   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.
2295 
2296   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 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.
2297 
2298   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.
2299 
2300   Example using 2 processes:
2301 $  NX (number of points on dm): 4
2302 $  sectionA                   : the on-disk section
2303 $  vecA                       : a vector associated with sectionA
2304 $  sectionB                   : sectiondm's local section constructed in this function
2305 $  vecB (local)               : a vector associated with sectiondm's local section
2306 $  vecB (global)              : a vector associated with sectiondm's global section
2307 $
2308 $                                     rank 0    rank 1
2309 $  vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2310 $  sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2311 $  sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2312 $  sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2313 $  [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2314 $  sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2315 $  sectionB->atlasDof             :     1 0 1 | 1 3
2316 $  sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2317 $  vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2318 $  vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2319 $
2320 $  where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2321 
2322 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`
2323 @*/
2324 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2325 {
2326   PetscBool ishdf5;
2327 
2328   PetscFunctionBegin;
2329   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2330   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2331   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2332   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2333   if (globalDofSF) PetscValidPointer(globalDofSF, 5);
2334   if (localDofSF) PetscValidPointer(localDofSF, 6);
2335   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2336   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2337   if (ishdf5) {
2338 #if defined(PETSC_HAVE_HDF5)
2339     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2340 #else
2341     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2342 #endif
2343   }
2344   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2345   PetscFunctionReturn(0);
2346 }
2347 
2348 /*@
2349   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2350 
2351   Collective on DM
2352 
2353   Input Parameters:
2354 + dm        - The DM that represents the topology
2355 . viewer    - The PetscViewer that represents the on-disk vector data
2356 . sectiondm - The DM that contains the global section on which vec is defined
2357 . sf        - The SF that migrates the on-disk vector data into vec
2358 - vec       - The global vector to set values of
2359 
2360   Level: advanced
2361 
2362   Notes:
2363   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 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.
2364 
2365   Typical calling sequence
2366 $       DMCreate(PETSC_COMM_WORLD, &dm);
2367 $       DMSetType(dm, DMPLEX);
2368 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2369 $       DMPlexTopologyLoad(dm, viewer, &sfX);
2370 $       DMClone(dm, &sectiondm);
2371 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2372 $       DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2373 $       DMGetGlobalVector(sectiondm, &vec);
2374 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2375 $       DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2376 $       DMRestoreGlobalVector(sectiondm, &vec);
2377 $       PetscSFDestroy(&gsf);
2378 $       PetscSFDestroy(&sfX);
2379 $       DMDestroy(&sectiondm);
2380 $       DMDestroy(&dm);
2381 
2382 .seealso: `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`
2383 @*/
2384 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2385 {
2386   PetscBool ishdf5;
2387 
2388   PetscFunctionBegin;
2389   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2390   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2391   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2392   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2393   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2394   /* Check consistency */
2395   {
2396     PetscSection section;
2397     PetscBool    includesConstraints;
2398     PetscInt     m, m1;
2399 
2400     PetscCall(VecGetLocalSize(vec, &m1));
2401     PetscCall(DMGetGlobalSection(sectiondm, &section));
2402     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2403     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2404     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2405     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2406   }
2407   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2408   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2409   if (ishdf5) {
2410 #if defined(PETSC_HAVE_HDF5)
2411     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2412 #else
2413     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2414 #endif
2415   }
2416   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2417   PetscFunctionReturn(0);
2418 }
2419 
2420 /*@
2421   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2422 
2423   Collective on DM
2424 
2425   Input Parameters:
2426 + dm        - The DM that represents the topology
2427 . viewer    - The PetscViewer that represents the on-disk vector data
2428 . sectiondm - The DM that contains the local section on which vec is defined
2429 . sf        - The SF that migrates the on-disk vector data into vec
2430 - vec       - The local vector to set values of
2431 
2432   Level: advanced
2433 
2434   Notes:
2435   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 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.
2436 
2437   Typical calling sequence
2438 $       DMCreate(PETSC_COMM_WORLD, &dm);
2439 $       DMSetType(dm, DMPLEX);
2440 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2441 $       DMPlexTopologyLoad(dm, viewer, &sfX);
2442 $       DMClone(dm, &sectiondm);
2443 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2444 $       DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2445 $       DMGetLocalVector(sectiondm, &vec);
2446 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2447 $       DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2448 $       DMRestoreLocalVector(sectiondm, &vec);
2449 $       PetscSFDestroy(&lsf);
2450 $       PetscSFDestroy(&sfX);
2451 $       DMDestroy(&sectiondm);
2452 $       DMDestroy(&dm);
2453 
2454 .seealso: `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`
2455 @*/
2456 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2457 {
2458   PetscBool ishdf5;
2459 
2460   PetscFunctionBegin;
2461   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2462   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2463   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2464   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2465   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2466   /* Check consistency */
2467   {
2468     PetscSection section;
2469     PetscBool    includesConstraints;
2470     PetscInt     m, m1;
2471 
2472     PetscCall(VecGetLocalSize(vec, &m1));
2473     PetscCall(DMGetLocalSection(sectiondm, &section));
2474     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2475     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2476     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2477     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2478   }
2479   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2480   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2481   if (ishdf5) {
2482 #if defined(PETSC_HAVE_HDF5)
2483     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2484 #else
2485     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2486 #endif
2487   }
2488   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2489   PetscFunctionReturn(0);
2490 }
2491 
2492 PetscErrorCode DMDestroy_Plex(DM dm)
2493 {
2494   DM_Plex *mesh = (DM_Plex *)dm->data;
2495 
2496   PetscFunctionBegin;
2497   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2498   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2499   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2500   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2501   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerviativeBoundaryValues_C", NULL));
2502   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2503   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2504   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2505   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2506   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2507   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2508   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2509   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2510   if (--mesh->refct > 0) PetscFunctionReturn(0);
2511   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2512   PetscCall(PetscFree(mesh->cones));
2513   PetscCall(PetscFree(mesh->coneOrientations));
2514   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2515   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2516   PetscCall(PetscFree(mesh->supports));
2517   PetscCall(PetscFree(mesh->facesTmp));
2518   PetscCall(PetscFree(mesh->tetgenOpts));
2519   PetscCall(PetscFree(mesh->triangleOpts));
2520   PetscCall(PetscFree(mesh->transformType));
2521   PetscCall(PetscFree(mesh->distributionName));
2522   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2523   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2524   PetscCall(ISDestroy(&mesh->subpointIS));
2525   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2526   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2527   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2528   PetscCall(ISDestroy(&mesh->anchorIS));
2529   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2530   PetscCall(PetscFree(mesh->parents));
2531   PetscCall(PetscFree(mesh->childIDs));
2532   PetscCall(PetscSectionDestroy(&mesh->childSection));
2533   PetscCall(PetscFree(mesh->children));
2534   PetscCall(DMDestroy(&mesh->referenceTree));
2535   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2536   PetscCall(PetscFree(mesh->neighbors));
2537   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2538   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2539   PetscCall(PetscFree(mesh));
2540   PetscFunctionReturn(0);
2541 }
2542 
2543 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2544 {
2545   PetscSection           sectionGlobal;
2546   PetscInt               bs = -1, mbs;
2547   PetscInt               localSize, localStart = 0;
2548   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2549   MatType                mtype;
2550   ISLocalToGlobalMapping ltog;
2551 
2552   PetscFunctionBegin;
2553   PetscCall(MatInitializePackage());
2554   mtype = dm->mattype;
2555   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2556   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2557   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2558   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2559   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2560   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2561   PetscCall(MatSetType(*J, mtype));
2562   PetscCall(MatSetFromOptions(*J));
2563   PetscCall(MatGetBlockSize(*J, &mbs));
2564   if (mbs > 1) bs = mbs;
2565   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2566   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2567   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2568   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2569   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2570   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2571   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2572   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2573   if (!isShell) {
2574     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2575     PetscInt *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2576     PetscInt  pStart, pEnd, p, dof, cdof;
2577 
2578     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2579 
2580     PetscCall(PetscCalloc1(localSize, &pblocks));
2581     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2582     for (p = pStart; p < pEnd; ++p) {
2583       PetscInt bdof, offset;
2584 
2585       PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2586       PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2587       PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2588       for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = dof - cdof;
2589       dof  = dof < 0 ? -(dof + 1) : dof;
2590       bdof = cdof && (dof - cdof) ? 1 : dof;
2591       if (dof) {
2592         if (bs < 0) {
2593           bs = bdof;
2594         } else if (bs != bdof) {
2595           bs = 1;
2596         }
2597       }
2598     }
2599     /* Must have same blocksize on all procs (some might have no points) */
2600     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2601     bsLocal[1] = bs;
2602     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2603     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2604     else bs = bsMinMax[0];
2605     bs = PetscMax(1, bs);
2606     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2607     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2608       PetscCall(MatSetBlockSize(*J, bs));
2609       PetscCall(MatSetUp(*J));
2610     } else {
2611       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2612       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2613       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2614     }
2615     { // Consolidate blocks
2616       PetscInt nblocks = 0;
2617       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2618         if (pblocks[i] == 0) continue;
2619         pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2620         for (PetscInt j = 1; j < pblocks[i]; j++) PetscCheck(pblocks[i + j] == pblocks[i], PETSC_COMM_SELF, PETSC_ERR_PLIB, "Block of size %" PetscInt_FMT " mismatches entry %" PetscInt_FMT, pblocks[i], pblocks[i + j]);
2621       }
2622       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2623     }
2624     PetscCall(PetscFree(pblocks));
2625   }
2626   PetscCall(MatSetDM(*J, dm));
2627   PetscFunctionReturn(0);
2628 }
2629 
2630 /*@
2631   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2632 
2633   Not collective
2634 
2635   Input Parameter:
2636 . mesh - The DMPlex
2637 
2638   Output Parameters:
2639 . subsection - The subdomain section
2640 
2641   Level: developer
2642 
2643 .seealso:
2644 @*/
2645 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2646 {
2647   DM_Plex *mesh = (DM_Plex *)dm->data;
2648 
2649   PetscFunctionBegin;
2650   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2651   if (!mesh->subdomainSection) {
2652     PetscSection section;
2653     PetscSF      sf;
2654 
2655     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2656     PetscCall(DMGetLocalSection(dm, &section));
2657     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2658     PetscCall(PetscSFDestroy(&sf));
2659   }
2660   *subsection = mesh->subdomainSection;
2661   PetscFunctionReturn(0);
2662 }
2663 
2664 /*@
2665   DMPlexGetChart - Return the interval for all mesh points [pStart, pEnd)
2666 
2667   Not collective
2668 
2669   Input Parameter:
2670 . mesh - The DMPlex
2671 
2672   Output Parameters:
2673 + pStart - The first mesh point
2674 - pEnd   - The upper bound for mesh points
2675 
2676   Level: beginner
2677 
2678 .seealso: `DMPlexCreate()`, `DMPlexSetChart()`
2679 @*/
2680 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2681 {
2682   DM_Plex *mesh = (DM_Plex *)dm->data;
2683 
2684   PetscFunctionBegin;
2685   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2686   PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2687   PetscFunctionReturn(0);
2688 }
2689 
2690 /*@
2691   DMPlexSetChart - Set the interval for all mesh points [pStart, pEnd)
2692 
2693   Not collective
2694 
2695   Input Parameters:
2696 + mesh - The DMPlex
2697 . pStart - The first mesh point
2698 - pEnd   - The upper bound for mesh points
2699 
2700   Output Parameters:
2701 
2702   Level: beginner
2703 
2704 .seealso: `DMPlexCreate()`, `DMPlexGetChart()`
2705 @*/
2706 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2707 {
2708   DM_Plex *mesh = (DM_Plex *)dm->data;
2709 
2710   PetscFunctionBegin;
2711   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2712   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2713   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2714   PetscFunctionReturn(0);
2715 }
2716 
2717 /*@
2718   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2719 
2720   Not collective
2721 
2722   Input Parameters:
2723 + mesh - The DMPlex
2724 - p - The point, which must lie in the chart set with DMPlexSetChart()
2725 
2726   Output Parameter:
2727 . size - The cone size for point p
2728 
2729   Level: beginner
2730 
2731 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2732 @*/
2733 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2734 {
2735   DM_Plex *mesh = (DM_Plex *)dm->data;
2736 
2737   PetscFunctionBegin;
2738   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2739   PetscValidIntPointer(size, 3);
2740   PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2741   PetscFunctionReturn(0);
2742 }
2743 
2744 /*@
2745   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2746 
2747   Not collective
2748 
2749   Input Parameters:
2750 + mesh - The DMPlex
2751 . p - The point, which must lie in the chart set with DMPlexSetChart()
2752 - size - The cone size for point p
2753 
2754   Output Parameter:
2755 
2756   Note:
2757   This should be called after DMPlexSetChart().
2758 
2759   Level: beginner
2760 
2761 .seealso: `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2762 @*/
2763 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
2764 {
2765   DM_Plex *mesh = (DM_Plex *)dm->data;
2766 
2767   PetscFunctionBegin;
2768   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2769   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
2770   PetscFunctionReturn(0);
2771 }
2772 
2773 /*@
2774   DMPlexAddConeSize - Add the given number of in-edges to this point in the DAG
2775 
2776   Not collective
2777 
2778   Input Parameters:
2779 + mesh - The DMPlex
2780 . p - The point, which must lie in the chart set with DMPlexSetChart()
2781 - size - The additional cone size for point p
2782 
2783   Output Parameter:
2784 
2785   Note:
2786   This should be called after DMPlexSetChart().
2787 
2788   Level: beginner
2789 
2790 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2791 @*/
2792 PetscErrorCode DMPlexAddConeSize(DM dm, PetscInt p, PetscInt size)
2793 {
2794   DM_Plex *mesh = (DM_Plex *)dm->data;
2795   PetscFunctionBegin;
2796   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2797   PetscCall(PetscSectionAddDof(mesh->coneSection, p, size));
2798   PetscFunctionReturn(0);
2799 }
2800 
2801 /*@C
2802   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
2803 
2804   Not collective
2805 
2806   Input Parameters:
2807 + dm - The DMPlex
2808 - p - The point, which must lie in the chart set with DMPlexSetChart()
2809 
2810   Output Parameter:
2811 . cone - An array of points which are on the in-edges for point p
2812 
2813   Level: beginner
2814 
2815   Fortran Notes:
2816   Since it returns an array, this routine is only available in Fortran 90, and you must
2817   include petsc.h90 in your code.
2818   You must also call DMPlexRestoreCone() after you finish using the returned array.
2819   DMPlexRestoreCone() is not needed/available in C.
2820 
2821 .seealso: `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`
2822 @*/
2823 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
2824 {
2825   DM_Plex *mesh = (DM_Plex *)dm->data;
2826   PetscInt off;
2827 
2828   PetscFunctionBegin;
2829   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2830   PetscValidPointer(cone, 3);
2831   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
2832   *cone = &mesh->cones[off];
2833   PetscFunctionReturn(0);
2834 }
2835 
2836 /*@C
2837   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
2838 
2839   Not collective
2840 
2841   Input Parameters:
2842 + dm - The DMPlex
2843 - p - The IS of points, which must lie in the chart set with DMPlexSetChart()
2844 
2845   Output Parameters:
2846 + pConesSection - PetscSection describing the layout of pCones
2847 - pCones - An array of points which are on the in-edges for the point set p
2848 
2849   Level: intermediate
2850 
2851 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`
2852 @*/
2853 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
2854 {
2855   PetscSection cs, newcs;
2856   PetscInt    *cones;
2857   PetscInt    *newarr = NULL;
2858   PetscInt     n;
2859 
2860   PetscFunctionBegin;
2861   PetscCall(DMPlexGetCones(dm, &cones));
2862   PetscCall(DMPlexGetConeSection(dm, &cs));
2863   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
2864   if (pConesSection) *pConesSection = newcs;
2865   if (pCones) {
2866     PetscCall(PetscSectionGetStorageSize(newcs, &n));
2867     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
2868   }
2869   PetscFunctionReturn(0);
2870 }
2871 
2872 /*@
2873   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
2874 
2875   Not collective
2876 
2877   Input Parameters:
2878 + dm - The DMPlex
2879 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2880 
2881   Output Parameter:
2882 . expandedPoints - An array of vertices recursively expanded from input points
2883 
2884   Level: advanced
2885 
2886   Notes:
2887   Like DMPlexGetConeRecursive but returns only the 0-depth IS (i.e. vertices only) and no sections.
2888   There is no corresponding Restore function, just call ISDestroy() on the returned IS to deallocate.
2889 
2890 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetDepth()`
2891 @*/
2892 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
2893 {
2894   IS      *expandedPointsAll;
2895   PetscInt depth;
2896 
2897   PetscFunctionBegin;
2898   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2899   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2900   PetscValidPointer(expandedPoints, 3);
2901   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2902   *expandedPoints = expandedPointsAll[0];
2903   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
2904   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2905   PetscFunctionReturn(0);
2906 }
2907 
2908 /*@
2909   DMPlexGetConeRecursive - Expand each given point into its cone points and do that recursively until we end up just with vertices (DAG points of depth 0, i.e. without cones).
2910 
2911   Not collective
2912 
2913   Input Parameters:
2914 + dm - The DMPlex
2915 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2916 
2917   Output Parameters:
2918 + depth - (optional) Size of the output arrays, equal to DMPlex depth, returned by DMPlexGetDepth()
2919 . expandedPoints - (optional) An array of index sets with recursively expanded cones
2920 - sections - (optional) An array of sections which describe mappings from points to their cone points
2921 
2922   Level: advanced
2923 
2924   Notes:
2925   Like DMPlexGetConeTuple() but recursive.
2926 
2927   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.
2928   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
2929 
2930   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:
2931   (1) DAG points in expandedPoints[d+1] with depth d+1 to their cone points in expandedPoints[d];
2932   (2) DAG points in expandedPoints[d+1] with depth in [0,d] to the same points in expandedPoints[d].
2933 
2934 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`, `DMPlexGetDepth()`
2935 @*/
2936 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2937 {
2938   const PetscInt *arr0 = NULL, *cone = NULL;
2939   PetscInt       *arr = NULL, *newarr = NULL;
2940   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
2941   IS             *expandedPoints_;
2942   PetscSection   *sections_;
2943 
2944   PetscFunctionBegin;
2945   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2946   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2947   if (depth) PetscValidIntPointer(depth, 3);
2948   if (expandedPoints) PetscValidPointer(expandedPoints, 4);
2949   if (sections) PetscValidPointer(sections, 5);
2950   PetscCall(ISGetLocalSize(points, &n));
2951   PetscCall(ISGetIndices(points, &arr0));
2952   PetscCall(DMPlexGetDepth(dm, &depth_));
2953   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
2954   PetscCall(PetscCalloc1(depth_, &sections_));
2955   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
2956   for (d = depth_ - 1; d >= 0; d--) {
2957     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
2958     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
2959     for (i = 0; i < n; i++) {
2960       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
2961       if (arr[i] >= start && arr[i] < end) {
2962         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
2963         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
2964       } else {
2965         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
2966       }
2967     }
2968     PetscCall(PetscSectionSetUp(sections_[d]));
2969     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
2970     PetscCall(PetscMalloc1(newn, &newarr));
2971     for (i = 0; i < n; i++) {
2972       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
2973       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
2974       if (cn > 1) {
2975         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
2976         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
2977       } else {
2978         newarr[co] = arr[i];
2979       }
2980     }
2981     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
2982     arr = newarr;
2983     n   = newn;
2984   }
2985   PetscCall(ISRestoreIndices(points, &arr0));
2986   *depth = depth_;
2987   if (expandedPoints) *expandedPoints = expandedPoints_;
2988   else {
2989     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
2990     PetscCall(PetscFree(expandedPoints_));
2991   }
2992   if (sections) *sections = sections_;
2993   else {
2994     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
2995     PetscCall(PetscFree(sections_));
2996   }
2997   PetscFunctionReturn(0);
2998 }
2999 
3000 /*@
3001   DMPlexRestoreConeRecursive - Deallocates arrays created by DMPlexGetConeRecursive
3002 
3003   Not collective
3004 
3005   Input Parameters:
3006 + dm - The DMPlex
3007 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
3008 
3009   Output Parameters:
3010 + depth - (optional) Size of the output arrays, equal to DMPlex depth, returned by DMPlexGetDepth()
3011 . expandedPoints - (optional) An array of recursively expanded cones
3012 - sections - (optional) An array of sections which describe mappings from points to their cone points
3013 
3014   Level: advanced
3015 
3016   Notes:
3017   See DMPlexGetConeRecursive() for details.
3018 
3019 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`, `DMPlexGetDepth()`
3020 @*/
3021 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3022 {
3023   PetscInt d, depth_;
3024 
3025   PetscFunctionBegin;
3026   PetscCall(DMPlexGetDepth(dm, &depth_));
3027   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3028   if (depth) *depth = 0;
3029   if (expandedPoints) {
3030     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3031     PetscCall(PetscFree(*expandedPoints));
3032   }
3033   if (sections) {
3034     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3035     PetscCall(PetscFree(*sections));
3036   }
3037   PetscFunctionReturn(0);
3038 }
3039 
3040 /*@
3041   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
3042 
3043   Not collective
3044 
3045   Input Parameters:
3046 + mesh - The DMPlex
3047 . p - The point, which must lie in the chart set with DMPlexSetChart()
3048 - cone - An array of points which are on the in-edges for point p
3049 
3050   Output Parameter:
3051 
3052   Note:
3053   This should be called after all calls to DMPlexSetConeSize() and DMSetUp().
3054 
3055   Level: beginner
3056 
3057 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3058 @*/
3059 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3060 {
3061   DM_Plex *mesh = (DM_Plex *)dm->data;
3062   PetscInt pStart, pEnd;
3063   PetscInt dof, off, c;
3064 
3065   PetscFunctionBegin;
3066   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3067   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3068   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3069   if (dof) PetscValidIntPointer(cone, 3);
3070   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3071   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);
3072   for (c = 0; c < dof; ++c) {
3073     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);
3074     mesh->cones[off + c] = cone[c];
3075   }
3076   PetscFunctionReturn(0);
3077 }
3078 
3079 /*@C
3080   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3081 
3082   Not collective
3083 
3084   Input Parameters:
3085 + mesh - The DMPlex
3086 - p - The point, which must lie in the chart set with DMPlexSetChart()
3087 
3088   Output Parameter:
3089 . coneOrientation - An array of orientations which are on the in-edges for point p. An orientation is an
3090                     integer giving the prescription for cone traversal.
3091 
3092   Level: beginner
3093 
3094   Notes:
3095   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3096   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3097   of o, however it is not necessarily the inverse. To get the inverse, use DMPolytopeTypeComposeOrientationInv()
3098   with the identity.
3099 
3100   Fortran Notes:
3101   Since it returns an array, this routine is only available in Fortran 90, and you must
3102   include petsc.h90 in your code.
3103   You must also call DMPlexRestoreConeOrientation() after you finish using the returned array.
3104   DMPlexRestoreConeOrientation() is not needed/available in C.
3105 
3106 .seealso: `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3107 @*/
3108 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3109 {
3110   DM_Plex *mesh = (DM_Plex *)dm->data;
3111   PetscInt off;
3112 
3113   PetscFunctionBegin;
3114   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3115   if (PetscDefined(USE_DEBUG)) {
3116     PetscInt dof;
3117     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3118     if (dof) PetscValidPointer(coneOrientation, 3);
3119   }
3120   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3121 
3122   *coneOrientation = &mesh->coneOrientations[off];
3123   PetscFunctionReturn(0);
3124 }
3125 
3126 /*@
3127   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3128 
3129   Not collective
3130 
3131   Input Parameters:
3132 + mesh - The DMPlex
3133 . p - The point, which must lie in the chart set with DMPlexSetChart()
3134 - coneOrientation - An array of orientations
3135   Output Parameter:
3136 
3137   Notes:
3138   This should be called after all calls to DMPlexSetConeSize() and DMSetUp().
3139 
3140   The meaning of coneOrientation is detailed in DMPlexGetConeOrientation().
3141 
3142   Level: beginner
3143 
3144 .seealso: `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3145 @*/
3146 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3147 {
3148   DM_Plex *mesh = (DM_Plex *)dm->data;
3149   PetscInt pStart, pEnd;
3150   PetscInt dof, off, c;
3151 
3152   PetscFunctionBegin;
3153   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3154   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3155   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3156   if (dof) PetscValidIntPointer(coneOrientation, 3);
3157   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3158   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);
3159   for (c = 0; c < dof; ++c) {
3160     PetscInt cdof, o = coneOrientation[c];
3161 
3162     PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3163     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);
3164     mesh->coneOrientations[off + c] = o;
3165   }
3166   PetscFunctionReturn(0);
3167 }
3168 
3169 /*@
3170   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3171 
3172   Not collective
3173 
3174   Input Parameters:
3175 + mesh - The DMPlex
3176 . p - The point, which must lie in the chart set with DMPlexSetChart()
3177 . conePos - The local index in the cone where the point should be put
3178 - conePoint - The mesh point to insert
3179 
3180   Level: beginner
3181 
3182 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3183 @*/
3184 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3185 {
3186   DM_Plex *mesh = (DM_Plex *)dm->data;
3187   PetscInt pStart, pEnd;
3188   PetscInt dof, off;
3189 
3190   PetscFunctionBegin;
3191   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3192   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3193   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);
3194   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);
3195   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3196   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3197   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);
3198   mesh->cones[off + conePos] = conePoint;
3199   PetscFunctionReturn(0);
3200 }
3201 
3202 /*@
3203   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3204 
3205   Not collective
3206 
3207   Input Parameters:
3208 + mesh - The DMPlex
3209 . p - The point, which must lie in the chart set with DMPlexSetChart()
3210 . conePos - The local index in the cone where the point should be put
3211 - coneOrientation - The point orientation to insert
3212 
3213   Level: beginner
3214 
3215   Notes:
3216   The meaning of coneOrientation values is detailed in DMPlexGetConeOrientation().
3217 
3218 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3219 @*/
3220 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3221 {
3222   DM_Plex *mesh = (DM_Plex *)dm->data;
3223   PetscInt pStart, pEnd;
3224   PetscInt dof, off;
3225 
3226   PetscFunctionBegin;
3227   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3228   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3229   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);
3230   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3231   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3232   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);
3233   mesh->coneOrientations[off + conePos] = coneOrientation;
3234   PetscFunctionReturn(0);
3235 }
3236 
3237 /*@
3238   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3239 
3240   Not collective
3241 
3242   Input Parameters:
3243 + mesh - The DMPlex
3244 - p - The point, which must lie in the chart set with DMPlexSetChart()
3245 
3246   Output Parameter:
3247 . size - The support size for point p
3248 
3249   Level: beginner
3250 
3251 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3252 @*/
3253 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3254 {
3255   DM_Plex *mesh = (DM_Plex *)dm->data;
3256 
3257   PetscFunctionBegin;
3258   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3259   PetscValidIntPointer(size, 3);
3260   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3261   PetscFunctionReturn(0);
3262 }
3263 
3264 /*@
3265   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3266 
3267   Not collective
3268 
3269   Input Parameters:
3270 + mesh - The DMPlex
3271 . p - The point, which must lie in the chart set with DMPlexSetChart()
3272 - size - The support size for point p
3273 
3274   Output Parameter:
3275 
3276   Note:
3277   This should be called after DMPlexSetChart().
3278 
3279   Level: beginner
3280 
3281 .seealso: `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3282 @*/
3283 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3284 {
3285   DM_Plex *mesh = (DM_Plex *)dm->data;
3286 
3287   PetscFunctionBegin;
3288   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3289   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3290   PetscFunctionReturn(0);
3291 }
3292 
3293 /*@C
3294   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3295 
3296   Not collective
3297 
3298   Input Parameters:
3299 + mesh - The DMPlex
3300 - p - The point, which must lie in the chart set with DMPlexSetChart()
3301 
3302   Output Parameter:
3303 . support - An array of points which are on the out-edges for point p
3304 
3305   Level: beginner
3306 
3307   Fortran Notes:
3308   Since it returns an array, this routine is only available in Fortran 90, and you must
3309   include petsc.h90 in your code.
3310   You must also call DMPlexRestoreSupport() after you finish using the returned array.
3311   DMPlexRestoreSupport() is not needed/available in C.
3312 
3313 .seealso: `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3314 @*/
3315 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3316 {
3317   DM_Plex *mesh = (DM_Plex *)dm->data;
3318   PetscInt off;
3319 
3320   PetscFunctionBegin;
3321   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3322   PetscValidPointer(support, 3);
3323   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3324   *support = &mesh->supports[off];
3325   PetscFunctionReturn(0);
3326 }
3327 
3328 /*@
3329   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3330 
3331   Not collective
3332 
3333   Input Parameters:
3334 + mesh - The DMPlex
3335 . p - The point, which must lie in the chart set with DMPlexSetChart()
3336 - support - An array of points which are on the out-edges for point p
3337 
3338   Output Parameter:
3339 
3340   Note:
3341   This should be called after all calls to DMPlexSetSupportSize() and DMSetUp().
3342 
3343   Level: beginner
3344 
3345 .seealso: `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3346 @*/
3347 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3348 {
3349   DM_Plex *mesh = (DM_Plex *)dm->data;
3350   PetscInt pStart, pEnd;
3351   PetscInt dof, off, c;
3352 
3353   PetscFunctionBegin;
3354   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3355   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3356   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3357   if (dof) PetscValidIntPointer(support, 3);
3358   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3359   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);
3360   for (c = 0; c < dof; ++c) {
3361     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);
3362     mesh->supports[off + c] = support[c];
3363   }
3364   PetscFunctionReturn(0);
3365 }
3366 
3367 /*@
3368   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3369 
3370   Not collective
3371 
3372   Input Parameters:
3373 + mesh - The DMPlex
3374 . p - The point, which must lie in the chart set with DMPlexSetChart()
3375 . supportPos - The local index in the cone where the point should be put
3376 - supportPoint - The mesh point to insert
3377 
3378   Level: beginner
3379 
3380 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3381 @*/
3382 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3383 {
3384   DM_Plex *mesh = (DM_Plex *)dm->data;
3385   PetscInt pStart, pEnd;
3386   PetscInt dof, off;
3387 
3388   PetscFunctionBegin;
3389   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3390   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3391   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3392   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3393   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);
3394   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);
3395   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);
3396   mesh->supports[off + supportPos] = supportPoint;
3397   PetscFunctionReturn(0);
3398 }
3399 
3400 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3401 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3402 {
3403   switch (ct) {
3404   case DM_POLYTOPE_SEGMENT:
3405     if (o == -1) return -2;
3406     break;
3407   case DM_POLYTOPE_TRIANGLE:
3408     if (o == -3) return -1;
3409     if (o == -2) return -3;
3410     if (o == -1) return -2;
3411     break;
3412   case DM_POLYTOPE_QUADRILATERAL:
3413     if (o == -4) return -2;
3414     if (o == -3) return -1;
3415     if (o == -2) return -4;
3416     if (o == -1) return -3;
3417     break;
3418   default:
3419     return o;
3420   }
3421   return o;
3422 }
3423 
3424 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3425 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3426 {
3427   switch (ct) {
3428   case DM_POLYTOPE_SEGMENT:
3429     if ((o == -2) || (o == 1)) return -1;
3430     if (o == -1) return 0;
3431     break;
3432   case DM_POLYTOPE_TRIANGLE:
3433     if (o == -3) return -2;
3434     if (o == -2) return -1;
3435     if (o == -1) return -3;
3436     break;
3437   case DM_POLYTOPE_QUADRILATERAL:
3438     if (o == -4) return -2;
3439     if (o == -3) return -1;
3440     if (o == -2) return -4;
3441     if (o == -1) return -3;
3442     break;
3443   default:
3444     return o;
3445   }
3446   return o;
3447 }
3448 
3449 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3450 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3451 {
3452   PetscInt pStart, pEnd, p;
3453 
3454   PetscFunctionBegin;
3455   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3456   for (p = pStart; p < pEnd; ++p) {
3457     const PetscInt *cone, *ornt;
3458     PetscInt        coneSize, c;
3459 
3460     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3461     PetscCall(DMPlexGetCone(dm, p, &cone));
3462     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3463     for (c = 0; c < coneSize; ++c) {
3464       DMPolytopeType ct;
3465       const PetscInt o = ornt[c];
3466 
3467       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3468       switch (ct) {
3469       case DM_POLYTOPE_SEGMENT:
3470         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3471         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3472         break;
3473       case DM_POLYTOPE_TRIANGLE:
3474         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3475         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3476         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3477         break;
3478       case DM_POLYTOPE_QUADRILATERAL:
3479         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3480         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3481         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3482         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3483         break;
3484       default:
3485         break;
3486       }
3487     }
3488   }
3489   PetscFunctionReturn(0);
3490 }
3491 
3492 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3493 {
3494   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3495   PetscInt       *closure;
3496   const PetscInt *tmp = NULL, *tmpO = NULL;
3497   PetscInt        off = 0, tmpSize, t;
3498 
3499   PetscFunctionBeginHot;
3500   if (ornt) {
3501     PetscCall(DMPlexGetCellType(dm, p, &ct));
3502     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3503   }
3504   if (*points) {
3505     closure = *points;
3506   } else {
3507     PetscInt maxConeSize, maxSupportSize;
3508     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3509     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3510   }
3511   if (useCone) {
3512     PetscCall(DMPlexGetConeSize(dm, p, &tmpSize));
3513     PetscCall(DMPlexGetCone(dm, p, &tmp));
3514     PetscCall(DMPlexGetConeOrientation(dm, p, &tmpO));
3515   } else {
3516     PetscCall(DMPlexGetSupportSize(dm, p, &tmpSize));
3517     PetscCall(DMPlexGetSupport(dm, p, &tmp));
3518   }
3519   if (ct == DM_POLYTOPE_UNKNOWN) {
3520     closure[off++] = p;
3521     closure[off++] = 0;
3522     for (t = 0; t < tmpSize; ++t) {
3523       closure[off++] = tmp[t];
3524       closure[off++] = tmpO ? tmpO[t] : 0;
3525     }
3526   } else {
3527     const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, ornt);
3528 
3529     /* We assume that cells with a valid type have faces with a valid type */
3530     closure[off++] = p;
3531     closure[off++] = ornt;
3532     for (t = 0; t < tmpSize; ++t) {
3533       DMPolytopeType ft;
3534 
3535       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3536       closure[off++] = tmp[arr[t]];
3537       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3538     }
3539   }
3540   if (numPoints) *numPoints = tmpSize + 1;
3541   if (points) *points = closure;
3542   PetscFunctionReturn(0);
3543 }
3544 
3545 /* We need a special tensor verison becasue we want to allow duplicate points in the endcaps for hybrid cells */
3546 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3547 {
3548   const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, o);
3549   const PetscInt *cone, *ornt;
3550   PetscInt       *pts, *closure = NULL;
3551   DMPolytopeType  ft;
3552   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3553   PetscInt        dim, coneSize, c, d, clSize, cl;
3554 
3555   PetscFunctionBeginHot;
3556   PetscCall(DMGetDimension(dm, &dim));
3557   PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
3558   PetscCall(DMPlexGetCone(dm, point, &cone));
3559   PetscCall(DMPlexGetConeOrientation(dm, point, &ornt));
3560   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3561   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3562   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3563   maxSize       = PetscMax(coneSeries, supportSeries);
3564   if (*points) {
3565     pts = *points;
3566   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3567   c        = 0;
3568   pts[c++] = point;
3569   pts[c++] = o;
3570   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3571   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3572   for (cl = 0; cl < clSize * 2; cl += 2) {
3573     pts[c++] = closure[cl];
3574     pts[c++] = closure[cl + 1];
3575   }
3576   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3577   for (cl = 0; cl < clSize * 2; cl += 2) {
3578     pts[c++] = closure[cl];
3579     pts[c++] = closure[cl + 1];
3580   }
3581   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3582   for (d = 2; d < coneSize; ++d) {
3583     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3584     pts[c++] = cone[arr[d * 2 + 0]];
3585     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3586   }
3587   if (dim >= 3) {
3588     for (d = 2; d < coneSize; ++d) {
3589       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
3590       const PetscInt *fcone, *fornt;
3591       PetscInt        fconeSize, fc, i;
3592 
3593       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3594       const PetscInt *farr = DMPolytopeTypeGetArrangment(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
3595       PetscCall(DMPlexGetConeSize(dm, fpoint, &fconeSize));
3596       PetscCall(DMPlexGetCone(dm, fpoint, &fcone));
3597       PetscCall(DMPlexGetConeOrientation(dm, fpoint, &fornt));
3598       for (fc = 0; fc < fconeSize; ++fc) {
3599         const PetscInt cp = fcone[farr[fc * 2 + 0]];
3600         const PetscInt co = farr[fc * 2 + 1];
3601 
3602         for (i = 0; i < c; i += 2)
3603           if (pts[i] == cp) break;
3604         if (i == c) {
3605           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3606           pts[c++] = cp;
3607           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
3608         }
3609       }
3610     }
3611   }
3612   *numPoints = c / 2;
3613   *points    = pts;
3614   PetscFunctionReturn(0);
3615 }
3616 
3617 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3618 {
3619   DMPolytopeType ct;
3620   PetscInt      *closure, *fifo;
3621   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3622   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3623   PetscInt       depth, maxSize;
3624 
3625   PetscFunctionBeginHot;
3626   PetscCall(DMPlexGetDepth(dm, &depth));
3627   if (depth == 1) {
3628     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3629     PetscFunctionReturn(0);
3630   }
3631   PetscCall(DMPlexGetCellType(dm, p, &ct));
3632   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3633   if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
3634     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3635     PetscFunctionReturn(0);
3636   }
3637   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3638   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
3639   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
3640   maxSize       = PetscMax(coneSeries, supportSeries);
3641   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3642   if (*points) {
3643     closure = *points;
3644   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
3645   closure[closureSize++] = p;
3646   closure[closureSize++] = ornt;
3647   fifo[fifoSize++]       = p;
3648   fifo[fifoSize++]       = ornt;
3649   fifo[fifoSize++]       = ct;
3650   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3651   while (fifoSize - fifoStart) {
3652     const PetscInt       q    = fifo[fifoStart++];
3653     const PetscInt       o    = fifo[fifoStart++];
3654     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
3655     const PetscInt      *qarr = DMPolytopeTypeGetArrangment(qt, o);
3656     const PetscInt      *tmp, *tmpO;
3657     PetscInt             tmpSize, t;
3658 
3659     if (PetscDefined(USE_DEBUG)) {
3660       PetscInt nO = DMPolytopeTypeGetNumArrangments(qt) / 2;
3661       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);
3662     }
3663     if (useCone) {
3664       PetscCall(DMPlexGetConeSize(dm, q, &tmpSize));
3665       PetscCall(DMPlexGetCone(dm, q, &tmp));
3666       PetscCall(DMPlexGetConeOrientation(dm, q, &tmpO));
3667     } else {
3668       PetscCall(DMPlexGetSupportSize(dm, q, &tmpSize));
3669       PetscCall(DMPlexGetSupport(dm, q, &tmp));
3670       tmpO = NULL;
3671     }
3672     for (t = 0; t < tmpSize; ++t) {
3673       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
3674       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
3675       const PetscInt cp = tmp[ip];
3676       PetscCall(DMPlexGetCellType(dm, cp, &ct));
3677       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
3678       PetscInt       c;
3679 
3680       /* Check for duplicate */
3681       for (c = 0; c < closureSize; c += 2) {
3682         if (closure[c] == cp) break;
3683       }
3684       if (c == closureSize) {
3685         closure[closureSize++] = cp;
3686         closure[closureSize++] = co;
3687         fifo[fifoSize++]       = cp;
3688         fifo[fifoSize++]       = co;
3689         fifo[fifoSize++]       = ct;
3690       }
3691     }
3692   }
3693   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3694   if (numPoints) *numPoints = closureSize / 2;
3695   if (points) *points = closure;
3696   PetscFunctionReturn(0);
3697 }
3698 
3699 /*@C
3700   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
3701 
3702   Not collective
3703 
3704   Input Parameters:
3705 + dm      - The DMPlex
3706 . p       - The mesh point
3707 - useCone - PETSC_TRUE for the closure, otherwise return the star
3708 
3709   Input/Output Parameter:
3710 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
3711            if NULL on input, internal storage will be returned, otherwise the provided array is used
3712 
3713   Output Parameter:
3714 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3715 
3716   Note:
3717   If using internal storage (points is NULL on input), each call overwrites the last output.
3718 
3719   Fortran Note:
3720   The numPoints argument is not present in the Fortran 90 binding since it is internal to the array.
3721 
3722   Level: beginner
3723 
3724 .seealso: `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3725 @*/
3726 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3727 {
3728   PetscFunctionBeginHot;
3729   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3730   if (numPoints) PetscValidIntPointer(numPoints, 4);
3731   if (points) PetscValidPointer(points, 5);
3732   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
3733   PetscFunctionReturn(0);
3734 }
3735 
3736 /*@C
3737   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
3738 
3739   Not collective
3740 
3741   Input Parameters:
3742 + dm        - The DMPlex
3743 . p         - The mesh point
3744 . useCone   - PETSC_TRUE for the closure, otherwise return the star
3745 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3746 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
3747 
3748   Note:
3749   If not using internal storage (points is not NULL on input), this call is unnecessary
3750 
3751   Level: beginner
3752 
3753 .seealso: `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3754 @*/
3755 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3756 {
3757   PetscFunctionBeginHot;
3758   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3759   if (numPoints) *numPoints = 0;
3760   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
3761   PetscFunctionReturn(0);
3762 }
3763 
3764 /*@
3765   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
3766 
3767   Not collective
3768 
3769   Input Parameter:
3770 . mesh - The DMPlex
3771 
3772   Output Parameters:
3773 + maxConeSize - The maximum number of in-edges
3774 - maxSupportSize - The maximum number of out-edges
3775 
3776   Level: beginner
3777 
3778 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3779 @*/
3780 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
3781 {
3782   DM_Plex *mesh = (DM_Plex *)dm->data;
3783 
3784   PetscFunctionBegin;
3785   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3786   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
3787   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
3788   PetscFunctionReturn(0);
3789 }
3790 
3791 PetscErrorCode DMSetUp_Plex(DM dm)
3792 {
3793   DM_Plex *mesh = (DM_Plex *)dm->data;
3794   PetscInt size, maxSupportSize;
3795 
3796   PetscFunctionBegin;
3797   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3798   PetscCall(PetscSectionSetUp(mesh->coneSection));
3799   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
3800   PetscCall(PetscMalloc1(size, &mesh->cones));
3801   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
3802   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
3803   if (maxSupportSize) {
3804     PetscCall(PetscSectionSetUp(mesh->supportSection));
3805     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
3806     PetscCall(PetscMalloc1(size, &mesh->supports));
3807   }
3808   PetscFunctionReturn(0);
3809 }
3810 
3811 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
3812 {
3813   PetscFunctionBegin;
3814   if (subdm) PetscCall(DMClone(dm, subdm));
3815   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, is, subdm));
3816   if (subdm) (*subdm)->useNatural = dm->useNatural;
3817   if (dm->useNatural && dm->sfMigration) {
3818     PetscSF sfNatural;
3819 
3820     (*subdm)->sfMigration = dm->sfMigration;
3821     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
3822     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
3823     (*subdm)->sfNatural = sfNatural;
3824   }
3825   PetscFunctionReturn(0);
3826 }
3827 
3828 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
3829 {
3830   PetscInt i = 0;
3831 
3832   PetscFunctionBegin;
3833   PetscCall(DMClone(dms[0], superdm));
3834   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
3835   (*superdm)->useNatural = PETSC_FALSE;
3836   for (i = 0; i < len; i++) {
3837     if (dms[i]->useNatural && dms[i]->sfMigration) {
3838       PetscSF sfNatural;
3839 
3840       (*superdm)->sfMigration = dms[i]->sfMigration;
3841       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
3842       (*superdm)->useNatural = PETSC_TRUE;
3843       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
3844       (*superdm)->sfNatural = sfNatural;
3845       break;
3846     }
3847   }
3848   PetscFunctionReturn(0);
3849 }
3850 
3851 /*@
3852   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
3853 
3854   Not collective
3855 
3856   Input Parameter:
3857 . mesh - The DMPlex
3858 
3859   Output Parameter:
3860 
3861   Note:
3862   This should be called after all calls to DMPlexSetCone()
3863 
3864   Level: beginner
3865 
3866 .seealso: `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
3867 @*/
3868 PetscErrorCode DMPlexSymmetrize(DM dm)
3869 {
3870   DM_Plex  *mesh = (DM_Plex *)dm->data;
3871   PetscInt *offsets;
3872   PetscInt  supportSize;
3873   PetscInt  pStart, pEnd, p;
3874 
3875   PetscFunctionBegin;
3876   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3877   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
3878   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
3879   /* Calculate support sizes */
3880   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3881   for (p = pStart; p < pEnd; ++p) {
3882     PetscInt dof, off, c;
3883 
3884     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3885     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3886     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
3887   }
3888   PetscCall(PetscSectionSetUp(mesh->supportSection));
3889   /* Calculate supports */
3890   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
3891   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
3892   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
3893   for (p = pStart; p < pEnd; ++p) {
3894     PetscInt dof, off, c;
3895 
3896     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3897     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3898     for (c = off; c < off + dof; ++c) {
3899       const PetscInt q = mesh->cones[c];
3900       PetscInt       offS;
3901 
3902       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
3903 
3904       mesh->supports[offS + offsets[q]] = p;
3905       ++offsets[q];
3906     }
3907   }
3908   PetscCall(PetscFree(offsets));
3909   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
3910   PetscFunctionReturn(0);
3911 }
3912 
3913 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
3914 {
3915   IS stratumIS;
3916 
3917   PetscFunctionBegin;
3918   if (pStart >= pEnd) PetscFunctionReturn(0);
3919   if (PetscDefined(USE_DEBUG)) {
3920     PetscInt  qStart, qEnd, numLevels, level;
3921     PetscBool overlap = PETSC_FALSE;
3922     PetscCall(DMLabelGetNumValues(label, &numLevels));
3923     for (level = 0; level < numLevels; level++) {
3924       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
3925       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
3926         overlap = PETSC_TRUE;
3927         break;
3928       }
3929     }
3930     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);
3931   }
3932   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
3933   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
3934   PetscCall(ISDestroy(&stratumIS));
3935   PetscFunctionReturn(0);
3936 }
3937 
3938 /*@
3939   DMPlexStratify - The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
3940   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram). The strata group all points of the
3941   same grade, and this function calculates the strata. This grade can be seen as the height (or depth) of the point in
3942   the DAG.
3943 
3944   Collective on dm
3945 
3946   Input Parameter:
3947 . mesh - The DMPlex
3948 
3949   Output Parameter:
3950 
3951   Notes:
3952   Concretely, DMPlexStratify() creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
3953   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
3954   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through DMPlexGetDepthLabel() or DMPlexGetDepthStratum(), or
3955   manually via DMGetLabel().  The height is defined implicitly by height = maxDimension - depth, and can be accessed
3956   via DMPlexGetHeightStratum().  For example, cells have height 0 and faces have height 1.
3957 
3958   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
3959   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
3960   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
3961   to interpolate only that one (e0), so that
3962 $  cone(c0) = {e0, v2}
3963 $  cone(e0) = {v0, v1}
3964   If DMPlexStratify() is run on this mesh, it will give depths
3965 $  depth 0 = {v0, v1, v2}
3966 $  depth 1 = {e0, c0}
3967   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
3968 
3969   DMPlexStratify() should be called after all calls to DMPlexSymmetrize()
3970 
3971   Level: beginner
3972 
3973 .seealso: `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
3974 @*/
3975 PetscErrorCode DMPlexStratify(DM dm)
3976 {
3977   DM_Plex *mesh = (DM_Plex *)dm->data;
3978   DMLabel  label;
3979   PetscInt pStart, pEnd, p;
3980   PetscInt numRoots = 0, numLeaves = 0;
3981 
3982   PetscFunctionBegin;
3983   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3984   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
3985 
3986   /* Create depth label */
3987   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3988   PetscCall(DMCreateLabel(dm, "depth"));
3989   PetscCall(DMPlexGetDepthLabel(dm, &label));
3990 
3991   {
3992     /* Initialize roots and count leaves */
3993     PetscInt sMin = PETSC_MAX_INT;
3994     PetscInt sMax = PETSC_MIN_INT;
3995     PetscInt coneSize, supportSize;
3996 
3997     for (p = pStart; p < pEnd; ++p) {
3998       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3999       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4000       if (!coneSize && supportSize) {
4001         sMin = PetscMin(p, sMin);
4002         sMax = PetscMax(p, sMax);
4003         ++numRoots;
4004       } else if (!supportSize && coneSize) {
4005         ++numLeaves;
4006       } else if (!supportSize && !coneSize) {
4007         /* Isolated points */
4008         sMin = PetscMin(p, sMin);
4009         sMax = PetscMax(p, sMax);
4010       }
4011     }
4012     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4013   }
4014 
4015   if (numRoots + numLeaves == (pEnd - pStart)) {
4016     PetscInt sMin = PETSC_MAX_INT;
4017     PetscInt sMax = PETSC_MIN_INT;
4018     PetscInt coneSize, supportSize;
4019 
4020     for (p = pStart; p < pEnd; ++p) {
4021       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4022       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4023       if (!supportSize && coneSize) {
4024         sMin = PetscMin(p, sMin);
4025         sMax = PetscMax(p, sMax);
4026       }
4027     }
4028     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4029   } else {
4030     PetscInt level = 0;
4031     PetscInt qStart, qEnd, q;
4032 
4033     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4034     while (qEnd > qStart) {
4035       PetscInt sMin = PETSC_MAX_INT;
4036       PetscInt sMax = PETSC_MIN_INT;
4037 
4038       for (q = qStart; q < qEnd; ++q) {
4039         const PetscInt *support;
4040         PetscInt        supportSize, s;
4041 
4042         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4043         PetscCall(DMPlexGetSupport(dm, q, &support));
4044         for (s = 0; s < supportSize; ++s) {
4045           sMin = PetscMin(support[s], sMin);
4046           sMax = PetscMax(support[s], sMax);
4047         }
4048       }
4049       PetscCall(DMLabelGetNumValues(label, &level));
4050       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4051       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4052     }
4053   }
4054   { /* just in case there is an empty process */
4055     PetscInt numValues, maxValues = 0, v;
4056 
4057     PetscCall(DMLabelGetNumValues(label, &numValues));
4058     PetscCallMPI(MPI_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4059     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4060   }
4061   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4062   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4063   PetscFunctionReturn(0);
4064 }
4065 
4066 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4067 {
4068   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4069   PetscInt       dim, depth, pheight, coneSize;
4070 
4071   PetscFunctionBeginHot;
4072   PetscCall(DMGetDimension(dm, &dim));
4073   PetscCall(DMPlexGetDepth(dm, &depth));
4074   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4075   pheight = depth - pdepth;
4076   if (depth <= 1) {
4077     switch (pdepth) {
4078     case 0:
4079       ct = DM_POLYTOPE_POINT;
4080       break;
4081     case 1:
4082       switch (coneSize) {
4083       case 2:
4084         ct = DM_POLYTOPE_SEGMENT;
4085         break;
4086       case 3:
4087         ct = DM_POLYTOPE_TRIANGLE;
4088         break;
4089       case 4:
4090         switch (dim) {
4091         case 2:
4092           ct = DM_POLYTOPE_QUADRILATERAL;
4093           break;
4094         case 3:
4095           ct = DM_POLYTOPE_TETRAHEDRON;
4096           break;
4097         default:
4098           break;
4099         }
4100         break;
4101       case 5:
4102         ct = DM_POLYTOPE_PYRAMID;
4103         break;
4104       case 6:
4105         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4106         break;
4107       case 8:
4108         ct = DM_POLYTOPE_HEXAHEDRON;
4109         break;
4110       default:
4111         break;
4112       }
4113     }
4114   } else {
4115     if (pdepth == 0) {
4116       ct = DM_POLYTOPE_POINT;
4117     } else if (pheight == 0) {
4118       switch (dim) {
4119       case 1:
4120         switch (coneSize) {
4121         case 2:
4122           ct = DM_POLYTOPE_SEGMENT;
4123           break;
4124         default:
4125           break;
4126         }
4127         break;
4128       case 2:
4129         switch (coneSize) {
4130         case 3:
4131           ct = DM_POLYTOPE_TRIANGLE;
4132           break;
4133         case 4:
4134           ct = DM_POLYTOPE_QUADRILATERAL;
4135           break;
4136         default:
4137           break;
4138         }
4139         break;
4140       case 3:
4141         switch (coneSize) {
4142         case 4:
4143           ct = DM_POLYTOPE_TETRAHEDRON;
4144           break;
4145         case 5: {
4146           const PetscInt *cone;
4147           PetscInt        faceConeSize;
4148 
4149           PetscCall(DMPlexGetCone(dm, p, &cone));
4150           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4151           switch (faceConeSize) {
4152           case 3:
4153             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4154             break;
4155           case 4:
4156             ct = DM_POLYTOPE_PYRAMID;
4157             break;
4158           }
4159         } break;
4160         case 6:
4161           ct = DM_POLYTOPE_HEXAHEDRON;
4162           break;
4163         default:
4164           break;
4165         }
4166         break;
4167       default:
4168         break;
4169       }
4170     } else if (pheight > 0) {
4171       switch (coneSize) {
4172       case 2:
4173         ct = DM_POLYTOPE_SEGMENT;
4174         break;
4175       case 3:
4176         ct = DM_POLYTOPE_TRIANGLE;
4177         break;
4178       case 4:
4179         ct = DM_POLYTOPE_QUADRILATERAL;
4180         break;
4181       default:
4182         break;
4183       }
4184     }
4185   }
4186   *pt = ct;
4187   PetscFunctionReturn(0);
4188 }
4189 
4190 /*@
4191   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4192 
4193   Collective on dm
4194 
4195   Input Parameter:
4196 . mesh - The DMPlex
4197 
4198   DMPlexComputeCellTypes() should be called after all calls to DMPlexSymmetrize() and DMPlexStratify()
4199 
4200   Level: developer
4201 
4202   Note: This function is normally called automatically by Plex when a cell type is requested. It creates an
4203   internal DMLabel named "celltype" which can be directly accessed using DMGetLabel(). A user may disable
4204   automatic creation by creating the label manually, using DMCreateLabel(dm, "celltype").
4205 
4206 .seealso: `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4207 @*/
4208 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4209 {
4210   DM_Plex *mesh;
4211   DMLabel  ctLabel;
4212   PetscInt pStart, pEnd, p;
4213 
4214   PetscFunctionBegin;
4215   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4216   mesh = (DM_Plex *)dm->data;
4217   PetscCall(DMCreateLabel(dm, "celltype"));
4218   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4219   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4220   for (p = pStart; p < pEnd; ++p) {
4221     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4222     PetscInt       pdepth;
4223 
4224     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4225     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4226     PetscCheck(ct != DM_POLYTOPE_UNKNOWN, PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " is screwed up", p);
4227     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4228   }
4229   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4230   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4231   PetscFunctionReturn(0);
4232 }
4233 
4234 /*@C
4235   DMPlexGetJoin - Get an array for the join of the set of points
4236 
4237   Not Collective
4238 
4239   Input Parameters:
4240 + dm - The DMPlex object
4241 . numPoints - The number of input points for the join
4242 - points - The input points
4243 
4244   Output Parameters:
4245 + numCoveredPoints - The number of points in the join
4246 - coveredPoints - The points in the join
4247 
4248   Level: intermediate
4249 
4250   Note: Currently, this is restricted to a single level join
4251 
4252   Fortran Notes:
4253   Since it returns an array, this routine is only available in Fortran 90, and you must
4254   include petsc.h90 in your code.
4255 
4256   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4257 
4258 .seealso: `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4259 @*/
4260 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4261 {
4262   DM_Plex  *mesh = (DM_Plex *)dm->data;
4263   PetscInt *join[2];
4264   PetscInt  joinSize, i = 0;
4265   PetscInt  dof, off, p, c, m;
4266   PetscInt  maxSupportSize;
4267 
4268   PetscFunctionBegin;
4269   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4270   PetscValidIntPointer(points, 3);
4271   PetscValidIntPointer(numCoveredPoints, 4);
4272   PetscValidPointer(coveredPoints, 5);
4273   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4274   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4275   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4276   /* Copy in support of first point */
4277   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4278   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4279   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4280   /* Check each successive support */
4281   for (p = 1; p < numPoints; ++p) {
4282     PetscInt newJoinSize = 0;
4283 
4284     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4285     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4286     for (c = 0; c < dof; ++c) {
4287       const PetscInt point = mesh->supports[off + c];
4288 
4289       for (m = 0; m < joinSize; ++m) {
4290         if (point == join[i][m]) {
4291           join[1 - i][newJoinSize++] = point;
4292           break;
4293         }
4294       }
4295     }
4296     joinSize = newJoinSize;
4297     i        = 1 - i;
4298   }
4299   *numCoveredPoints = joinSize;
4300   *coveredPoints    = join[i];
4301   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4302   PetscFunctionReturn(0);
4303 }
4304 
4305 /*@C
4306   DMPlexRestoreJoin - Restore an array for the join of the set of points
4307 
4308   Not Collective
4309 
4310   Input Parameters:
4311 + dm - The DMPlex object
4312 . numPoints - The number of input points for the join
4313 - points - The input points
4314 
4315   Output Parameters:
4316 + numCoveredPoints - The number of points in the join
4317 - coveredPoints - The points in the join
4318 
4319   Fortran Notes:
4320   Since it returns an array, this routine is only available in Fortran 90, and you must
4321   include petsc.h90 in your code.
4322 
4323   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4324 
4325   Level: intermediate
4326 
4327 .seealso: `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4328 @*/
4329 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4330 {
4331   PetscFunctionBegin;
4332   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4333   if (points) PetscValidIntPointer(points, 3);
4334   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints, 4);
4335   PetscValidPointer(coveredPoints, 5);
4336   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4337   if (numCoveredPoints) *numCoveredPoints = 0;
4338   PetscFunctionReturn(0);
4339 }
4340 
4341 /*@C
4342   DMPlexGetFullJoin - Get an array for the join of the set of points
4343 
4344   Not Collective
4345 
4346   Input Parameters:
4347 + dm - The DMPlex object
4348 . numPoints - The number of input points for the join
4349 - points - The input points
4350 
4351   Output Parameters:
4352 + numCoveredPoints - The number of points in the join
4353 - coveredPoints - The points in the join
4354 
4355   Fortran Notes:
4356   Since it returns an array, this routine is only available in Fortran 90, and you must
4357   include petsc.h90 in your code.
4358 
4359   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4360 
4361   Level: intermediate
4362 
4363 .seealso: `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4364 @*/
4365 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4366 {
4367   PetscInt *offsets, **closures;
4368   PetscInt *join[2];
4369   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4370   PetscInt  p, d, c, m, ms;
4371 
4372   PetscFunctionBegin;
4373   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4374   PetscValidIntPointer(points, 3);
4375   PetscValidIntPointer(numCoveredPoints, 4);
4376   PetscValidPointer(coveredPoints, 5);
4377 
4378   PetscCall(DMPlexGetDepth(dm, &depth));
4379   PetscCall(PetscCalloc1(numPoints, &closures));
4380   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4381   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4382   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4383   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4384   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4385 
4386   for (p = 0; p < numPoints; ++p) {
4387     PetscInt closureSize;
4388 
4389     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4390 
4391     offsets[p * (depth + 2) + 0] = 0;
4392     for (d = 0; d < depth + 1; ++d) {
4393       PetscInt pStart, pEnd, i;
4394 
4395       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4396       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4397         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4398           offsets[p * (depth + 2) + d + 1] = i;
4399           break;
4400         }
4401       }
4402       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4403     }
4404     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);
4405   }
4406   for (d = 0; d < depth + 1; ++d) {
4407     PetscInt dof;
4408 
4409     /* Copy in support of first point */
4410     dof = offsets[d + 1] - offsets[d];
4411     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4412     /* Check each successive cone */
4413     for (p = 1; p < numPoints && joinSize; ++p) {
4414       PetscInt newJoinSize = 0;
4415 
4416       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4417       for (c = 0; c < dof; ++c) {
4418         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4419 
4420         for (m = 0; m < joinSize; ++m) {
4421           if (point == join[i][m]) {
4422             join[1 - i][newJoinSize++] = point;
4423             break;
4424           }
4425         }
4426       }
4427       joinSize = newJoinSize;
4428       i        = 1 - i;
4429     }
4430     if (joinSize) break;
4431   }
4432   *numCoveredPoints = joinSize;
4433   *coveredPoints    = join[i];
4434   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4435   PetscCall(PetscFree(closures));
4436   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4437   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4438   PetscFunctionReturn(0);
4439 }
4440 
4441 /*@C
4442   DMPlexGetMeet - Get an array for the meet of the set of points
4443 
4444   Not Collective
4445 
4446   Input Parameters:
4447 + dm - The DMPlex object
4448 . numPoints - The number of input points for the meet
4449 - points - The input points
4450 
4451   Output Parameters:
4452 + numCoveredPoints - The number of points in the meet
4453 - coveredPoints - The points in the meet
4454 
4455   Level: intermediate
4456 
4457   Note: Currently, this is restricted to a single level meet
4458 
4459   Fortran Notes:
4460   Since it returns an array, this routine is only available in Fortran 90, and you must
4461   include petsc.h90 in your code.
4462 
4463   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4464 
4465 .seealso: `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4466 @*/
4467 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4468 {
4469   DM_Plex  *mesh = (DM_Plex *)dm->data;
4470   PetscInt *meet[2];
4471   PetscInt  meetSize, i = 0;
4472   PetscInt  dof, off, p, c, m;
4473   PetscInt  maxConeSize;
4474 
4475   PetscFunctionBegin;
4476   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4477   PetscValidIntPointer(points, 3);
4478   PetscValidIntPointer(numCoveringPoints, 4);
4479   PetscValidPointer(coveringPoints, 5);
4480   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4481   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4482   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4483   /* Copy in cone of first point */
4484   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4485   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4486   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4487   /* Check each successive cone */
4488   for (p = 1; p < numPoints; ++p) {
4489     PetscInt newMeetSize = 0;
4490 
4491     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4492     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4493     for (c = 0; c < dof; ++c) {
4494       const PetscInt point = mesh->cones[off + c];
4495 
4496       for (m = 0; m < meetSize; ++m) {
4497         if (point == meet[i][m]) {
4498           meet[1 - i][newMeetSize++] = point;
4499           break;
4500         }
4501       }
4502     }
4503     meetSize = newMeetSize;
4504     i        = 1 - i;
4505   }
4506   *numCoveringPoints = meetSize;
4507   *coveringPoints    = meet[i];
4508   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4509   PetscFunctionReturn(0);
4510 }
4511 
4512 /*@C
4513   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4514 
4515   Not Collective
4516 
4517   Input Parameters:
4518 + dm - The DMPlex object
4519 . numPoints - The number of input points for the meet
4520 - points - The input points
4521 
4522   Output Parameters:
4523 + numCoveredPoints - The number of points in the meet
4524 - coveredPoints - The points in the meet
4525 
4526   Level: intermediate
4527 
4528   Fortran Notes:
4529   Since it returns an array, this routine is only available in Fortran 90, and you must
4530   include petsc.h90 in your code.
4531 
4532   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4533 
4534 .seealso: `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4535 @*/
4536 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4537 {
4538   PetscFunctionBegin;
4539   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4540   if (points) PetscValidIntPointer(points, 3);
4541   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints, 4);
4542   PetscValidPointer(coveredPoints, 5);
4543   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4544   if (numCoveredPoints) *numCoveredPoints = 0;
4545   PetscFunctionReturn(0);
4546 }
4547 
4548 /*@C
4549   DMPlexGetFullMeet - Get an array for the meet of the set of points
4550 
4551   Not Collective
4552 
4553   Input Parameters:
4554 + dm - The DMPlex object
4555 . numPoints - The number of input points for the meet
4556 - points - The input points
4557 
4558   Output Parameters:
4559 + numCoveredPoints - The number of points in the meet
4560 - coveredPoints - The points in the meet
4561 
4562   Level: intermediate
4563 
4564   Fortran Notes:
4565   Since it returns an array, this routine is only available in Fortran 90, and you must
4566   include petsc.h90 in your code.
4567 
4568   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4569 
4570 .seealso: `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4571 @*/
4572 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4573 {
4574   PetscInt *offsets, **closures;
4575   PetscInt *meet[2];
4576   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
4577   PetscInt  p, h, c, m, mc;
4578 
4579   PetscFunctionBegin;
4580   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4581   PetscValidIntPointer(points, 3);
4582   PetscValidIntPointer(numCoveredPoints, 4);
4583   PetscValidPointer(coveredPoints, 5);
4584 
4585   PetscCall(DMPlexGetDepth(dm, &height));
4586   PetscCall(PetscMalloc1(numPoints, &closures));
4587   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4588   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4589   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
4590   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4591   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4592 
4593   for (p = 0; p < numPoints; ++p) {
4594     PetscInt closureSize;
4595 
4596     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4597 
4598     offsets[p * (height + 2) + 0] = 0;
4599     for (h = 0; h < height + 1; ++h) {
4600       PetscInt pStart, pEnd, i;
4601 
4602       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4603       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
4604         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4605           offsets[p * (height + 2) + h + 1] = i;
4606           break;
4607         }
4608       }
4609       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
4610     }
4611     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);
4612   }
4613   for (h = 0; h < height + 1; ++h) {
4614     PetscInt dof;
4615 
4616     /* Copy in cone of first point */
4617     dof = offsets[h + 1] - offsets[h];
4618     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
4619     /* Check each successive cone */
4620     for (p = 1; p < numPoints && meetSize; ++p) {
4621       PetscInt newMeetSize = 0;
4622 
4623       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
4624       for (c = 0; c < dof; ++c) {
4625         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
4626 
4627         for (m = 0; m < meetSize; ++m) {
4628           if (point == meet[i][m]) {
4629             meet[1 - i][newMeetSize++] = point;
4630             break;
4631           }
4632         }
4633       }
4634       meetSize = newMeetSize;
4635       i        = 1 - i;
4636     }
4637     if (meetSize) break;
4638   }
4639   *numCoveredPoints = meetSize;
4640   *coveredPoints    = meet[i];
4641   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
4642   PetscCall(PetscFree(closures));
4643   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4644   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
4645   PetscFunctionReturn(0);
4646 }
4647 
4648 /*@C
4649   DMPlexEqual - Determine if two DMs have the same topology
4650 
4651   Not Collective
4652 
4653   Input Parameters:
4654 + dmA - A DMPlex object
4655 - dmB - A DMPlex object
4656 
4657   Output Parameters:
4658 . equal - PETSC_TRUE if the topologies are identical
4659 
4660   Level: intermediate
4661 
4662   Notes:
4663   We are not solving graph isomorphism, so we do not permutation.
4664 
4665 .seealso: `DMPlexGetCone()`
4666 @*/
4667 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
4668 {
4669   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
4670 
4671   PetscFunctionBegin;
4672   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
4673   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
4674   PetscValidBoolPointer(equal, 3);
4675 
4676   *equal = PETSC_FALSE;
4677   PetscCall(DMPlexGetDepth(dmA, &depth));
4678   PetscCall(DMPlexGetDepth(dmB, &depthB));
4679   if (depth != depthB) PetscFunctionReturn(0);
4680   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
4681   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
4682   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(0);
4683   for (p = pStart; p < pEnd; ++p) {
4684     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
4685     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
4686 
4687     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
4688     PetscCall(DMPlexGetCone(dmA, p, &cone));
4689     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
4690     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
4691     PetscCall(DMPlexGetCone(dmB, p, &coneB));
4692     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
4693     if (coneSize != coneSizeB) PetscFunctionReturn(0);
4694     for (c = 0; c < coneSize; ++c) {
4695       if (cone[c] != coneB[c]) PetscFunctionReturn(0);
4696       if (ornt[c] != orntB[c]) PetscFunctionReturn(0);
4697     }
4698     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
4699     PetscCall(DMPlexGetSupport(dmA, p, &support));
4700     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
4701     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
4702     if (supportSize != supportSizeB) PetscFunctionReturn(0);
4703     for (s = 0; s < supportSize; ++s) {
4704       if (support[s] != supportB[s]) PetscFunctionReturn(0);
4705     }
4706   }
4707   *equal = PETSC_TRUE;
4708   PetscFunctionReturn(0);
4709 }
4710 
4711 /*@C
4712   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
4713 
4714   Not Collective
4715 
4716   Input Parameters:
4717 + dm         - The DMPlex
4718 . cellDim    - The cell dimension
4719 - numCorners - The number of vertices on a cell
4720 
4721   Output Parameters:
4722 . numFaceVertices - The number of vertices on a face
4723 
4724   Level: developer
4725 
4726   Notes:
4727   Of course this can only work for a restricted set of symmetric shapes
4728 
4729 .seealso: `DMPlexGetCone()`
4730 @*/
4731 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
4732 {
4733   MPI_Comm comm;
4734 
4735   PetscFunctionBegin;
4736   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
4737   PetscValidIntPointer(numFaceVertices, 4);
4738   switch (cellDim) {
4739   case 0:
4740     *numFaceVertices = 0;
4741     break;
4742   case 1:
4743     *numFaceVertices = 1;
4744     break;
4745   case 2:
4746     switch (numCorners) {
4747     case 3:                 /* triangle */
4748       *numFaceVertices = 2; /* Edge has 2 vertices */
4749       break;
4750     case 4:                 /* quadrilateral */
4751       *numFaceVertices = 2; /* Edge has 2 vertices */
4752       break;
4753     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
4754       *numFaceVertices = 3; /* Edge has 3 vertices */
4755       break;
4756     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
4757       *numFaceVertices = 3; /* Edge has 3 vertices */
4758       break;
4759     default:
4760       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4761     }
4762     break;
4763   case 3:
4764     switch (numCorners) {
4765     case 4:                 /* tetradehdron */
4766       *numFaceVertices = 3; /* Face has 3 vertices */
4767       break;
4768     case 6:                 /* tet cohesive cells */
4769       *numFaceVertices = 4; /* Face has 4 vertices */
4770       break;
4771     case 8:                 /* hexahedron */
4772       *numFaceVertices = 4; /* Face has 4 vertices */
4773       break;
4774     case 9:                 /* tet cohesive Lagrange cells */
4775       *numFaceVertices = 6; /* Face has 6 vertices */
4776       break;
4777     case 10:                /* quadratic tetrahedron */
4778       *numFaceVertices = 6; /* Face has 6 vertices */
4779       break;
4780     case 12:                /* hex cohesive Lagrange cells */
4781       *numFaceVertices = 6; /* Face has 6 vertices */
4782       break;
4783     case 18:                /* quadratic tet cohesive Lagrange cells */
4784       *numFaceVertices = 6; /* Face has 6 vertices */
4785       break;
4786     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
4787       *numFaceVertices = 9; /* Face has 9 vertices */
4788       break;
4789     default:
4790       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4791     }
4792     break;
4793   default:
4794     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
4795   }
4796   PetscFunctionReturn(0);
4797 }
4798 
4799 /*@
4800   DMPlexGetDepthLabel - Get the DMLabel recording the depth of each point
4801 
4802   Not Collective
4803 
4804   Input Parameter:
4805 . dm    - The DMPlex object
4806 
4807   Output Parameter:
4808 . depthLabel - The DMLabel recording point depth
4809 
4810   Level: developer
4811 
4812 .seealso: `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
4813 @*/
4814 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
4815 {
4816   PetscFunctionBegin;
4817   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4818   PetscValidPointer(depthLabel, 2);
4819   *depthLabel = dm->depthLabel;
4820   PetscFunctionReturn(0);
4821 }
4822 
4823 /*@
4824   DMPlexGetDepth - Get the depth of the DAG representing this mesh
4825 
4826   Not Collective
4827 
4828   Input Parameter:
4829 . dm    - The DMPlex object
4830 
4831   Output Parameter:
4832 . depth - The number of strata (breadth first levels) in the DAG
4833 
4834   Level: developer
4835 
4836   Notes:
4837   This returns maximum of point depths over all points, i.e. maximum value of the label returned by DMPlexGetDepthLabel().
4838   The point depth is described more in detail in DMPlexGetDepthStratum().
4839   An empty mesh gives -1.
4840 
4841 .seealso: `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
4842 @*/
4843 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
4844 {
4845   DMLabel  label;
4846   PetscInt d = 0;
4847 
4848   PetscFunctionBegin;
4849   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4850   PetscValidIntPointer(depth, 2);
4851   PetscCall(DMPlexGetDepthLabel(dm, &label));
4852   if (label) PetscCall(DMLabelGetNumValues(label, &d));
4853   *depth = d - 1;
4854   PetscFunctionReturn(0);
4855 }
4856 
4857 /*@
4858   DMPlexGetDepthStratum - Get the bounds [start, end) for all points at a certain depth.
4859 
4860   Not Collective
4861 
4862   Input Parameters:
4863 + dm    - The DMPlex object
4864 - depth - The requested depth
4865 
4866   Output Parameters:
4867 + start - The first point at this depth
4868 - end   - One beyond the last point at this depth
4869 
4870   Notes:
4871   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
4872   often "vertices".  If the mesh is "interpolated" (see DMPlexInterpolate()), then depth stratum 1 contains the next
4873   higher dimension, e.g., "edges".
4874 
4875   Level: developer
4876 
4877 .seealso: `DMPlexGetHeightStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
4878 @*/
4879 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
4880 {
4881   DMLabel  label;
4882   PetscInt pStart, pEnd;
4883 
4884   PetscFunctionBegin;
4885   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4886   if (start) {
4887     PetscValidIntPointer(start, 3);
4888     *start = 0;
4889   }
4890   if (end) {
4891     PetscValidIntPointer(end, 4);
4892     *end = 0;
4893   }
4894   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4895   if (pStart == pEnd) PetscFunctionReturn(0);
4896   if (depth < 0) {
4897     if (start) *start = pStart;
4898     if (end) *end = pEnd;
4899     PetscFunctionReturn(0);
4900   }
4901   PetscCall(DMPlexGetDepthLabel(dm, &label));
4902   PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
4903   PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
4904   PetscFunctionReturn(0);
4905 }
4906 
4907 /*@
4908   DMPlexGetHeightStratum - Get the bounds [start, end) for all points at a certain height.
4909 
4910   Not Collective
4911 
4912   Input Parameters:
4913 + dm     - The DMPlex object
4914 - height - The requested height
4915 
4916   Output Parameters:
4917 + start - The first point at this height
4918 - end   - One beyond the last point at this height
4919 
4920   Notes:
4921   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
4922   points, often called "cells" or "elements".  If the mesh is "interpolated" (see DMPlexInterpolate()), then height
4923   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
4924 
4925   Level: developer
4926 
4927 .seealso: `DMPlexGetDepthStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
4928 @*/
4929 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
4930 {
4931   DMLabel  label;
4932   PetscInt depth, pStart, pEnd;
4933 
4934   PetscFunctionBegin;
4935   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4936   if (start) {
4937     PetscValidIntPointer(start, 3);
4938     *start = 0;
4939   }
4940   if (end) {
4941     PetscValidIntPointer(end, 4);
4942     *end = 0;
4943   }
4944   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4945   if (pStart == pEnd) PetscFunctionReturn(0);
4946   if (height < 0) {
4947     if (start) *start = pStart;
4948     if (end) *end = pEnd;
4949     PetscFunctionReturn(0);
4950   }
4951   PetscCall(DMPlexGetDepthLabel(dm, &label));
4952   PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
4953   PetscCall(DMLabelGetNumValues(label, &depth));
4954   PetscCall(DMLabelGetStratumBounds(label, depth - 1 - height, start, end));
4955   PetscFunctionReturn(0);
4956 }
4957 
4958 /*@
4959   DMPlexGetPointDepth - Get the depth of a given point
4960 
4961   Not Collective
4962 
4963   Input Parameters:
4964 + dm    - The DMPlex object
4965 - point - The point
4966 
4967   Output Parameter:
4968 . depth - The depth of the point
4969 
4970   Level: intermediate
4971 
4972 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
4973 @*/
4974 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
4975 {
4976   PetscFunctionBegin;
4977   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4978   PetscValidIntPointer(depth, 3);
4979   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
4980   PetscFunctionReturn(0);
4981 }
4982 
4983 /*@
4984   DMPlexGetPointHeight - Get the height of a given point
4985 
4986   Not Collective
4987 
4988   Input Parameters:
4989 + dm    - The DMPlex object
4990 - point - The point
4991 
4992   Output Parameter:
4993 . height - The height of the point
4994 
4995   Level: intermediate
4996 
4997 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
4998 @*/
4999 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5000 {
5001   PetscInt n, pDepth;
5002 
5003   PetscFunctionBegin;
5004   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5005   PetscValidIntPointer(height, 3);
5006   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5007   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5008   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5009   PetscFunctionReturn(0);
5010 }
5011 
5012 /*@
5013   DMPlexGetCellTypeLabel - Get the DMLabel recording the polytope type of each cell
5014 
5015   Not Collective
5016 
5017   Input Parameter:
5018 . dm - The DMPlex object
5019 
5020   Output Parameter:
5021 . celltypeLabel - The DMLabel recording cell polytope type
5022 
5023   Note: This function will trigger automatica computation of cell types. This can be disabled by calling
5024   DMCreateLabel(dm, "celltype") beforehand.
5025 
5026   Level: developer
5027 
5028 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5029 @*/
5030 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5031 {
5032   PetscFunctionBegin;
5033   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5034   PetscValidPointer(celltypeLabel, 2);
5035   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5036   *celltypeLabel = dm->celltypeLabel;
5037   PetscFunctionReturn(0);
5038 }
5039 
5040 /*@
5041   DMPlexGetCellType - Get the polytope type of a given cell
5042 
5043   Not Collective
5044 
5045   Input Parameters:
5046 + dm   - The DMPlex object
5047 - cell - The cell
5048 
5049   Output Parameter:
5050 . celltype - The polytope type of the cell
5051 
5052   Level: intermediate
5053 
5054 .seealso: `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5055 @*/
5056 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5057 {
5058   DMLabel  label;
5059   PetscInt ct;
5060 
5061   PetscFunctionBegin;
5062   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5063   PetscValidPointer(celltype, 3);
5064   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5065   PetscCall(DMLabelGetValue(label, cell, &ct));
5066   PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5067   *celltype = (DMPolytopeType)ct;
5068   PetscFunctionReturn(0);
5069 }
5070 
5071 /*@
5072   DMPlexSetCellType - Set the polytope type of a given cell
5073 
5074   Not Collective
5075 
5076   Input Parameters:
5077 + dm   - The DMPlex object
5078 . cell - The cell
5079 - celltype - The polytope type of the cell
5080 
5081   Note: By default, cell types will be automatically computed using DMPlexComputeCellTypes() before this function
5082   is executed. This function will override the computed type. However, if automatic classification will not succeed
5083   and a user wants to manually specify all types, the classification must be disabled by calling
5084   DMCreaateLabel(dm, "celltype") before getting or setting any cell types.
5085 
5086   Level: advanced
5087 
5088 .seealso: `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5089 @*/
5090 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5091 {
5092   DMLabel label;
5093 
5094   PetscFunctionBegin;
5095   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5096   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5097   PetscCall(DMLabelSetValue(label, cell, celltype));
5098   PetscFunctionReturn(0);
5099 }
5100 
5101 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5102 {
5103   PetscSection section, s;
5104   Mat          m;
5105   PetscInt     maxHeight;
5106 
5107   PetscFunctionBegin;
5108   PetscCall(DMClone(dm, cdm));
5109   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5110   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5111   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5112   PetscCall(DMSetLocalSection(*cdm, section));
5113   PetscCall(PetscSectionDestroy(&section));
5114   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
5115   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
5116   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
5117   PetscCall(PetscSectionDestroy(&s));
5118   PetscCall(MatDestroy(&m));
5119 
5120   PetscCall(DMSetNumFields(*cdm, 1));
5121   PetscCall(DMCreateDS(*cdm));
5122   PetscFunctionReturn(0);
5123 }
5124 
5125 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5126 {
5127   Vec coordsLocal, cellCoordsLocal;
5128   DM  coordsDM, cellCoordsDM;
5129 
5130   PetscFunctionBegin;
5131   *field = NULL;
5132   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5133   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5134   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5135   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5136   if (coordsLocal && coordsDM) {
5137     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5138     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5139   }
5140   PetscFunctionReturn(0);
5141 }
5142 
5143 /*@C
5144   DMPlexGetConeSection - Return a section which describes the layout of cone data
5145 
5146   Not Collective
5147 
5148   Input Parameters:
5149 . dm        - The DMPlex object
5150 
5151   Output Parameter:
5152 . section - The PetscSection object
5153 
5154   Level: developer
5155 
5156 .seealso: `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`
5157 @*/
5158 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5159 {
5160   DM_Plex *mesh = (DM_Plex *)dm->data;
5161 
5162   PetscFunctionBegin;
5163   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5164   if (section) *section = mesh->coneSection;
5165   PetscFunctionReturn(0);
5166 }
5167 
5168 /*@C
5169   DMPlexGetSupportSection - Return a section which describes the layout of support data
5170 
5171   Not Collective
5172 
5173   Input Parameters:
5174 . dm        - The DMPlex object
5175 
5176   Output Parameter:
5177 . section - The PetscSection object
5178 
5179   Level: developer
5180 
5181 .seealso: `DMPlexGetConeSection()`
5182 @*/
5183 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5184 {
5185   DM_Plex *mesh = (DM_Plex *)dm->data;
5186 
5187   PetscFunctionBegin;
5188   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5189   if (section) *section = mesh->supportSection;
5190   PetscFunctionReturn(0);
5191 }
5192 
5193 /*@C
5194   DMPlexGetCones - Return cone data
5195 
5196   Not Collective
5197 
5198   Input Parameters:
5199 . dm        - The DMPlex object
5200 
5201   Output Parameter:
5202 . cones - The cone for each point
5203 
5204   Level: developer
5205 
5206 .seealso: `DMPlexGetConeSection()`
5207 @*/
5208 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5209 {
5210   DM_Plex *mesh = (DM_Plex *)dm->data;
5211 
5212   PetscFunctionBegin;
5213   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5214   if (cones) *cones = mesh->cones;
5215   PetscFunctionReturn(0);
5216 }
5217 
5218 /*@C
5219   DMPlexGetConeOrientations - Return cone orientation data
5220 
5221   Not Collective
5222 
5223   Input Parameters:
5224 . dm        - The DMPlex object
5225 
5226   Output Parameter:
5227 . coneOrientations - The array of cone orientations for all points
5228 
5229   Level: developer
5230 
5231   Notes:
5232   The PetscSection returned by DMPlexGetConeSection() partitions coneOrientations into cone orientations of particular points as returned by DMPlexGetConeOrientation().
5233 
5234   The meaning of coneOrientations values is detailed in DMPlexGetConeOrientation().
5235 
5236 .seealso: `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`
5237 @*/
5238 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5239 {
5240   DM_Plex *mesh = (DM_Plex *)dm->data;
5241 
5242   PetscFunctionBegin;
5243   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5244   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5245   PetscFunctionReturn(0);
5246 }
5247 
5248 /******************************** FEM Support **********************************/
5249 
5250 /*
5251  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5252  representing a line in the section.
5253 */
5254 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(PetscSection section, PetscInt field, PetscInt line, PetscBool vertexchart, PetscInt *Nc, PetscInt *k)
5255 {
5256   PetscFunctionBeginHot;
5257   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5258   if (line < 0) {
5259     *k  = 0;
5260     *Nc = 0;
5261   } else if (vertexchart) { /* If we only have a vertex chart, we must have degree k=1 */
5262     *k = 1;
5263   } else { /* Assume the full interpolated mesh is in the chart; lines in particular */
5264     /* An order k SEM disc has k-1 dofs on an edge */
5265     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5266     *k = *k / *Nc + 1;
5267   }
5268   PetscFunctionReturn(0);
5269 }
5270 
5271 /*@
5272 
5273   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5274   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5275   section provided (or the section of the DM).
5276 
5277   Input Parameters:
5278 + dm      - The DM
5279 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or PETSC_DETERMINE
5280 - section - The PetscSection to reorder, or NULL for the default section
5281 
5282   Note: The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5283   degree of the basis.
5284 
5285   Example:
5286   A typical interpolated single-quad mesh might order points as
5287 .vb
5288   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5289 
5290   v4 -- e6 -- v3
5291   |           |
5292   e7    c0    e8
5293   |           |
5294   v1 -- e5 -- v2
5295 .ve
5296 
5297   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5298   dofs in the order of points, e.g.,
5299 .vb
5300     c0 -> [0,1,2,3]
5301     v1 -> [4]
5302     ...
5303     e5 -> [8, 9]
5304 .ve
5305 
5306   which corresponds to the dofs
5307 .vb
5308     6   10  11  7
5309     13  2   3   15
5310     12  0   1   14
5311     4   8   9   5
5312 .ve
5313 
5314   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5315 .vb
5316   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5317 .ve
5318 
5319   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5320 .vb
5321    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5322 .ve
5323 
5324   Level: developer
5325 
5326 .seealso: `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5327 @*/
5328 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5329 {
5330   DMLabel   label;
5331   PetscInt  dim, depth = -1, eStart = -1, Nf;
5332   PetscBool vertexchart;
5333 
5334   PetscFunctionBegin;
5335   PetscCall(DMGetDimension(dm, &dim));
5336   if (dim < 1) PetscFunctionReturn(0);
5337   if (point < 0) {
5338     PetscInt sStart, sEnd;
5339 
5340     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5341     point = sEnd - sStart ? sStart : point;
5342   }
5343   PetscCall(DMPlexGetDepthLabel(dm, &label));
5344   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5345   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5346   if (depth == 1) {
5347     eStart = point;
5348   } else if (depth == dim) {
5349     const PetscInt *cone;
5350 
5351     PetscCall(DMPlexGetCone(dm, point, &cone));
5352     if (dim == 2) eStart = cone[0];
5353     else if (dim == 3) {
5354       const PetscInt *cone2;
5355       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5356       eStart = cone2[0];
5357     } 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);
5358   } 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);
5359   { /* Determine whether the chart covers all points or just vertices. */
5360     PetscInt pStart, pEnd, cStart, cEnd;
5361     PetscCall(DMPlexGetDepthStratum(dm, 0, &pStart, &pEnd));
5362     PetscCall(PetscSectionGetChart(section, &cStart, &cEnd));
5363     if (pStart == cStart && pEnd == cEnd) vertexchart = PETSC_TRUE;      /* Only vertices are in the chart */
5364     else if (cStart <= point && point < cEnd) vertexchart = PETSC_FALSE; /* Some interpolated points exist in the chart */
5365     else vertexchart = PETSC_TRUE;                                       /* Some interpolated points are not in chart; assume dofs only at cells and vertices */
5366   }
5367   PetscCall(PetscSectionGetNumFields(section, &Nf));
5368   for (PetscInt d = 1; d <= dim; d++) {
5369     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5370     PetscInt *perm;
5371 
5372     for (f = 0; f < Nf; ++f) {
5373       PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5374       size += PetscPowInt(k + 1, d) * Nc;
5375     }
5376     PetscCall(PetscMalloc1(size, &perm));
5377     for (f = 0; f < Nf; ++f) {
5378       switch (d) {
5379       case 1:
5380         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5381         /*
5382          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5383          We want              [ vtx0; edge of length k-1; vtx1 ]
5384          */
5385         for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5386         for (i = 0; i < k - 1; i++)
5387           for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5388         for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5389         foffset = offset;
5390         break;
5391       case 2:
5392         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5393         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5394         /* The SEM order is
5395 
5396          v_lb, {e_b}, v_rb,
5397          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5398          v_lt, reverse {e_t}, v_rt
5399          */
5400         {
5401           const PetscInt of   = 0;
5402           const PetscInt oeb  = of + PetscSqr(k - 1);
5403           const PetscInt oer  = oeb + (k - 1);
5404           const PetscInt oet  = oer + (k - 1);
5405           const PetscInt oel  = oet + (k - 1);
5406           const PetscInt ovlb = oel + (k - 1);
5407           const PetscInt ovrb = ovlb + 1;
5408           const PetscInt ovrt = ovrb + 1;
5409           const PetscInt ovlt = ovrt + 1;
5410           PetscInt       o;
5411 
5412           /* bottom */
5413           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5414           for (o = oeb; o < oer; ++o)
5415             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5416           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
5417           /* middle */
5418           for (i = 0; i < k - 1; ++i) {
5419             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
5420             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
5421               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5422             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
5423           }
5424           /* top */
5425           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
5426           for (o = oel - 1; o >= oet; --o)
5427             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5428           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
5429           foffset = offset;
5430         }
5431         break;
5432       case 3:
5433         /* The original hex closure is
5434 
5435          {c,
5436          f_b, f_t, f_f, f_b, f_r, f_l,
5437          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5438          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5439          */
5440         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5441         /* The SEM order is
5442          Bottom Slice
5443          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5444          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5445          v_blb, {e_bb}, v_brb,
5446 
5447          Middle Slice (j)
5448          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5449          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5450          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5451 
5452          Top Slice
5453          v_tlf, {e_tf}, v_trf,
5454          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5455          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5456          */
5457         {
5458           const PetscInt oc    = 0;
5459           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
5460           const PetscInt oft   = ofb + PetscSqr(k - 1);
5461           const PetscInt off   = oft + PetscSqr(k - 1);
5462           const PetscInt ofk   = off + PetscSqr(k - 1);
5463           const PetscInt ofr   = ofk + PetscSqr(k - 1);
5464           const PetscInt ofl   = ofr + PetscSqr(k - 1);
5465           const PetscInt oebl  = ofl + PetscSqr(k - 1);
5466           const PetscInt oebb  = oebl + (k - 1);
5467           const PetscInt oebr  = oebb + (k - 1);
5468           const PetscInt oebf  = oebr + (k - 1);
5469           const PetscInt oetf  = oebf + (k - 1);
5470           const PetscInt oetr  = oetf + (k - 1);
5471           const PetscInt oetb  = oetr + (k - 1);
5472           const PetscInt oetl  = oetb + (k - 1);
5473           const PetscInt oerf  = oetl + (k - 1);
5474           const PetscInt oelf  = oerf + (k - 1);
5475           const PetscInt oelb  = oelf + (k - 1);
5476           const PetscInt oerb  = oelb + (k - 1);
5477           const PetscInt ovblf = oerb + (k - 1);
5478           const PetscInt ovblb = ovblf + 1;
5479           const PetscInt ovbrb = ovblb + 1;
5480           const PetscInt ovbrf = ovbrb + 1;
5481           const PetscInt ovtlf = ovbrf + 1;
5482           const PetscInt ovtrf = ovtlf + 1;
5483           const PetscInt ovtrb = ovtrf + 1;
5484           const PetscInt ovtlb = ovtrb + 1;
5485           PetscInt       o, n;
5486 
5487           /* Bottom Slice */
5488           /*   bottom */
5489           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
5490           for (o = oetf - 1; o >= oebf; --o)
5491             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5492           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
5493           /*   middle */
5494           for (i = 0; i < k - 1; ++i) {
5495             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
5496             for (n = 0; n < k - 1; ++n) {
5497               o = ofb + n * (k - 1) + i;
5498               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5499             }
5500             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
5501           }
5502           /*   top */
5503           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
5504           for (o = oebb; o < oebr; ++o)
5505             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5506           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
5507 
5508           /* Middle Slice */
5509           for (j = 0; j < k - 1; ++j) {
5510             /*   bottom */
5511             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
5512             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
5513               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5514             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
5515             /*   middle */
5516             for (i = 0; i < k - 1; ++i) {
5517               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
5518               for (n = 0; n < k - 1; ++n)
5519                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
5520               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
5521             }
5522             /*   top */
5523             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
5524             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
5525               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5526             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
5527           }
5528 
5529           /* Top Slice */
5530           /*   bottom */
5531           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
5532           for (o = oetf; o < oetr; ++o)
5533             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5534           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
5535           /*   middle */
5536           for (i = 0; i < k - 1; ++i) {
5537             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
5538             for (n = 0; n < k - 1; ++n)
5539               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
5540             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
5541           }
5542           /*   top */
5543           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
5544           for (o = oetl - 1; o >= oetb; --o)
5545             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5546           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
5547 
5548           foffset = offset;
5549         }
5550         break;
5551       default:
5552         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
5553       }
5554     }
5555     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
5556     /* Check permutation */
5557     {
5558       PetscInt *check;
5559 
5560       PetscCall(PetscMalloc1(size, &check));
5561       for (i = 0; i < size; ++i) {
5562         check[i] = -1;
5563         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
5564       }
5565       for (i = 0; i < size; ++i) check[perm[i]] = i;
5566       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
5567       PetscCall(PetscFree(check));
5568     }
5569     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
5570     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
5571       PetscInt *loc_perm;
5572       PetscCall(PetscMalloc1(size * 2, &loc_perm));
5573       for (PetscInt i = 0; i < size; i++) {
5574         loc_perm[i]        = perm[i];
5575         loc_perm[size + i] = size + perm[i];
5576       }
5577       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
5578     }
5579   }
5580   PetscFunctionReturn(0);
5581 }
5582 
5583 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
5584 {
5585   PetscDS  prob;
5586   PetscInt depth, Nf, h;
5587   DMLabel  label;
5588 
5589   PetscFunctionBeginHot;
5590   PetscCall(DMGetDS(dm, &prob));
5591   Nf      = prob->Nf;
5592   label   = dm->depthLabel;
5593   *dspace = NULL;
5594   if (field < Nf) {
5595     PetscObject disc = prob->disc[field];
5596 
5597     if (disc->classid == PETSCFE_CLASSID) {
5598       PetscDualSpace dsp;
5599 
5600       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
5601       PetscCall(DMLabelGetNumValues(label, &depth));
5602       PetscCall(DMLabelGetValue(label, point, &h));
5603       h = depth - 1 - h;
5604       if (h) {
5605         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
5606       } else {
5607         *dspace = dsp;
5608       }
5609     }
5610   }
5611   PetscFunctionReturn(0);
5612 }
5613 
5614 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5615 {
5616   PetscScalar       *array;
5617   const PetscScalar *vArray;
5618   const PetscInt    *cone, *coneO;
5619   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
5620 
5621   PetscFunctionBeginHot;
5622   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5623   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
5624   PetscCall(DMPlexGetCone(dm, point, &cone));
5625   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
5626   if (!values || !*values) {
5627     if ((point >= pStart) && (point < pEnd)) {
5628       PetscInt dof;
5629 
5630       PetscCall(PetscSectionGetDof(section, point, &dof));
5631       size += dof;
5632     }
5633     for (p = 0; p < numPoints; ++p) {
5634       const PetscInt cp = cone[p];
5635       PetscInt       dof;
5636 
5637       if ((cp < pStart) || (cp >= pEnd)) continue;
5638       PetscCall(PetscSectionGetDof(section, cp, &dof));
5639       size += dof;
5640     }
5641     if (!values) {
5642       if (csize) *csize = size;
5643       PetscFunctionReturn(0);
5644     }
5645     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
5646   } else {
5647     array = *values;
5648   }
5649   size = 0;
5650   PetscCall(VecGetArrayRead(v, &vArray));
5651   if ((point >= pStart) && (point < pEnd)) {
5652     PetscInt           dof, off, d;
5653     const PetscScalar *varr;
5654 
5655     PetscCall(PetscSectionGetDof(section, point, &dof));
5656     PetscCall(PetscSectionGetOffset(section, point, &off));
5657     varr = &vArray[off];
5658     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5659     size += dof;
5660   }
5661   for (p = 0; p < numPoints; ++p) {
5662     const PetscInt     cp = cone[p];
5663     PetscInt           o  = coneO[p];
5664     PetscInt           dof, off, d;
5665     const PetscScalar *varr;
5666 
5667     if ((cp < pStart) || (cp >= pEnd)) continue;
5668     PetscCall(PetscSectionGetDof(section, cp, &dof));
5669     PetscCall(PetscSectionGetOffset(section, cp, &off));
5670     varr = &vArray[off];
5671     if (o >= 0) {
5672       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5673     } else {
5674       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
5675     }
5676     size += dof;
5677   }
5678   PetscCall(VecRestoreArrayRead(v, &vArray));
5679   if (!*values) {
5680     if (csize) *csize = size;
5681     *values = array;
5682   } else {
5683     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5684     *csize = size;
5685   }
5686   PetscFunctionReturn(0);
5687 }
5688 
5689 /* Compress out points not in the section */
5690 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
5691 {
5692   const PetscInt np = *numPoints;
5693   PetscInt       pStart, pEnd, p, q;
5694 
5695   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5696   for (p = 0, q = 0; p < np; ++p) {
5697     const PetscInt r = points[p * 2];
5698     if ((r >= pStart) && (r < pEnd)) {
5699       points[q * 2]     = r;
5700       points[q * 2 + 1] = points[p * 2 + 1];
5701       ++q;
5702     }
5703   }
5704   *numPoints = q;
5705   return 0;
5706 }
5707 
5708 /* Compressed closure does not apply closure permutation */
5709 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5710 {
5711   const PetscInt *cla = NULL;
5712   PetscInt        np, *pts = NULL;
5713 
5714   PetscFunctionBeginHot;
5715   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
5716   if (*clPoints) {
5717     PetscInt dof, off;
5718 
5719     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
5720     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
5721     PetscCall(ISGetIndices(*clPoints, &cla));
5722     np  = dof / 2;
5723     pts = (PetscInt *)&cla[off];
5724   } else {
5725     PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &np, &pts));
5726     PetscCall(CompressPoints_Private(section, &np, pts));
5727   }
5728   *numPoints = np;
5729   *points    = pts;
5730   *clp       = cla;
5731   PetscFunctionReturn(0);
5732 }
5733 
5734 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5735 {
5736   PetscFunctionBeginHot;
5737   if (!*clPoints) {
5738     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
5739   } else {
5740     PetscCall(ISRestoreIndices(*clPoints, clp));
5741   }
5742   *numPoints = 0;
5743   *points    = NULL;
5744   *clSec     = NULL;
5745   *clPoints  = NULL;
5746   *clp       = NULL;
5747   PetscFunctionReturn(0);
5748 }
5749 
5750 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
5751 {
5752   PetscInt            offset = 0, p;
5753   const PetscInt    **perms  = NULL;
5754   const PetscScalar **flips  = NULL;
5755 
5756   PetscFunctionBeginHot;
5757   *size = 0;
5758   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
5759   for (p = 0; p < numPoints; p++) {
5760     const PetscInt     point = points[2 * p];
5761     const PetscInt    *perm  = perms ? perms[p] : NULL;
5762     const PetscScalar *flip  = flips ? flips[p] : NULL;
5763     PetscInt           dof, off, d;
5764     const PetscScalar *varr;
5765 
5766     PetscCall(PetscSectionGetDof(section, point, &dof));
5767     PetscCall(PetscSectionGetOffset(section, point, &off));
5768     varr = &vArray[off];
5769     if (clperm) {
5770       if (perm) {
5771         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
5772       } else {
5773         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
5774       }
5775       if (flip) {
5776         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
5777       }
5778     } else {
5779       if (perm) {
5780         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
5781       } else {
5782         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
5783       }
5784       if (flip) {
5785         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
5786       }
5787     }
5788     offset += dof;
5789   }
5790   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
5791   *size = offset;
5792   PetscFunctionReturn(0);
5793 }
5794 
5795 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[])
5796 {
5797   PetscInt offset = 0, f;
5798 
5799   PetscFunctionBeginHot;
5800   *size = 0;
5801   for (f = 0; f < numFields; ++f) {
5802     PetscInt            p;
5803     const PetscInt    **perms = NULL;
5804     const PetscScalar **flips = NULL;
5805 
5806     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
5807     for (p = 0; p < numPoints; p++) {
5808       const PetscInt     point = points[2 * p];
5809       PetscInt           fdof, foff, b;
5810       const PetscScalar *varr;
5811       const PetscInt    *perm = perms ? perms[p] : NULL;
5812       const PetscScalar *flip = flips ? flips[p] : NULL;
5813 
5814       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
5815       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
5816       varr = &vArray[foff];
5817       if (clperm) {
5818         if (perm) {
5819           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
5820         } else {
5821           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
5822         }
5823         if (flip) {
5824           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
5825         }
5826       } else {
5827         if (perm) {
5828           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
5829         } else {
5830           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
5831         }
5832         if (flip) {
5833           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
5834         }
5835       }
5836       offset += fdof;
5837     }
5838     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
5839   }
5840   *size = offset;
5841   PetscFunctionReturn(0);
5842 }
5843 
5844 /*@C
5845   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
5846 
5847   Not collective
5848 
5849   Input Parameters:
5850 + dm - The DM
5851 . section - The section describing the layout in v, or NULL to use the default section
5852 . v - The local vector
5853 - point - The point in the DM
5854 
5855   Input/Output Parameters:
5856 + csize  - The size of the input values array, or NULL; on output the number of values in the closure
5857 - values - An array to use for the values, or NULL to have it allocated automatically;
5858            if the user provided NULL, it is a borrowed array and should not be freed
5859 
5860 $ Note that DMPlexVecGetClosure/DMPlexVecRestoreClosure only allocates the values array if it set to NULL in the
5861 $ calling function. This is because DMPlexVecGetClosure() is typically called in the inner loop of a Vec or Mat
5862 $ assembly function, and a user may already have allocated storage for this operation.
5863 $
5864 $ A typical use could be
5865 $
5866 $  values = NULL;
5867 $  PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5868 $  for (cl = 0; cl < clSize; ++cl) {
5869 $    <Compute on closure>
5870 $  }
5871 $  PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
5872 $
5873 $ or
5874 $
5875 $  PetscMalloc1(clMaxSize, &values);
5876 $  for (p = pStart; p < pEnd; ++p) {
5877 $    clSize = clMaxSize;
5878 $    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5879 $    for (cl = 0; cl < clSize; ++cl) {
5880 $      <Compute on closure>
5881 $    }
5882 $  }
5883 $  PetscFree(values);
5884 
5885   Fortran Notes:
5886   Since it returns an array, this routine is only available in Fortran 90, and you must
5887   include petsc.h90 in your code.
5888 
5889   The csize argument is not present in the Fortran 90 binding since it is internal to the array.
5890 
5891   Level: intermediate
5892 
5893 .seealso `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
5894 @*/
5895 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5896 {
5897   PetscSection    clSection;
5898   IS              clPoints;
5899   PetscInt       *points = NULL;
5900   const PetscInt *clp, *perm;
5901   PetscInt        depth, numFields, numPoints, asize;
5902 
5903   PetscFunctionBeginHot;
5904   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5905   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5906   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5907   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5908   PetscCall(DMPlexGetDepth(dm, &depth));
5909   PetscCall(PetscSectionGetNumFields(section, &numFields));
5910   if (depth == 1 && numFields < 2) {
5911     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
5912     PetscFunctionReturn(0);
5913   }
5914   /* Get points */
5915   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
5916   /* Get sizes */
5917   asize = 0;
5918   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
5919     PetscInt dof;
5920     PetscCall(PetscSectionGetDof(section, points[p], &dof));
5921     asize += dof;
5922   }
5923   if (values) {
5924     const PetscScalar *vArray;
5925     PetscInt           size;
5926 
5927     if (*values) {
5928       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);
5929     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
5930     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
5931     PetscCall(VecGetArrayRead(v, &vArray));
5932     /* Get values */
5933     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
5934     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
5935     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
5936     /* Cleanup array */
5937     PetscCall(VecRestoreArrayRead(v, &vArray));
5938   }
5939   if (csize) *csize = asize;
5940   /* Cleanup points */
5941   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
5942   PetscFunctionReturn(0);
5943 }
5944 
5945 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
5946 {
5947   DMLabel            depthLabel;
5948   PetscSection       clSection;
5949   IS                 clPoints;
5950   PetscScalar       *array;
5951   const PetscScalar *vArray;
5952   PetscInt          *points = NULL;
5953   const PetscInt    *clp, *perm = NULL;
5954   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
5955 
5956   PetscFunctionBeginHot;
5957   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5958   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5959   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5960   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5961   PetscCall(DMPlexGetDepth(dm, &mdepth));
5962   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5963   PetscCall(PetscSectionGetNumFields(section, &numFields));
5964   if (mdepth == 1 && numFields < 2) {
5965     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
5966     PetscFunctionReturn(0);
5967   }
5968   /* Get points */
5969   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
5970   for (clsize = 0, p = 0; p < Np; p++) {
5971     PetscInt dof;
5972     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
5973     clsize += dof;
5974   }
5975   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
5976   /* Filter points */
5977   for (p = 0; p < numPoints * 2; p += 2) {
5978     PetscInt dep;
5979 
5980     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
5981     if (dep != depth) continue;
5982     points[Np * 2 + 0] = points[p];
5983     points[Np * 2 + 1] = points[p + 1];
5984     ++Np;
5985   }
5986   /* Get array */
5987   if (!values || !*values) {
5988     PetscInt asize = 0, dof;
5989 
5990     for (p = 0; p < Np * 2; p += 2) {
5991       PetscCall(PetscSectionGetDof(section, points[p], &dof));
5992       asize += dof;
5993     }
5994     if (!values) {
5995       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
5996       if (csize) *csize = asize;
5997       PetscFunctionReturn(0);
5998     }
5999     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6000   } else {
6001     array = *values;
6002   }
6003   PetscCall(VecGetArrayRead(v, &vArray));
6004   /* Get values */
6005   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6006   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6007   /* Cleanup points */
6008   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6009   /* Cleanup array */
6010   PetscCall(VecRestoreArrayRead(v, &vArray));
6011   if (!*values) {
6012     if (csize) *csize = size;
6013     *values = array;
6014   } else {
6015     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6016     *csize = size;
6017   }
6018   PetscFunctionReturn(0);
6019 }
6020 
6021 /*@C
6022   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
6023 
6024   Not collective
6025 
6026   Input Parameters:
6027 + dm - The DM
6028 . section - The section describing the layout in v, or NULL to use the default section
6029 . v - The local vector
6030 . point - The point in the DM
6031 . csize - The number of values in the closure, or NULL
6032 - values - The array of values, which is a borrowed array and should not be freed
6033 
6034   Note that the array values are discarded and not copied back into v. In order to copy values back to v, use DMPlexVecSetClosure()
6035 
6036   Fortran Notes:
6037   Since it returns an array, this routine is only available in Fortran 90, and you must
6038   include petsc.h90 in your code.
6039 
6040   The csize argument is not present in the Fortran 90 binding since it is internal to the array.
6041 
6042   Level: intermediate
6043 
6044 .seealso `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6045 @*/
6046 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6047 {
6048   PetscInt size = 0;
6049 
6050   PetscFunctionBegin;
6051   /* Should work without recalculating size */
6052   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6053   *values = NULL;
6054   PetscFunctionReturn(0);
6055 }
6056 
6057 static inline void add(PetscScalar *x, PetscScalar y)
6058 {
6059   *x += y;
6060 }
6061 static inline void insert(PetscScalar *x, PetscScalar y)
6062 {
6063   *x = y;
6064 }
6065 
6066 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[])
6067 {
6068   PetscInt        cdof;  /* The number of constraints on this point */
6069   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6070   PetscScalar    *a;
6071   PetscInt        off, cind = 0, k;
6072 
6073   PetscFunctionBegin;
6074   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6075   PetscCall(PetscSectionGetOffset(section, point, &off));
6076   a = &array[off];
6077   if (!cdof || setBC) {
6078     if (clperm) {
6079       if (perm) {
6080         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6081       } else {
6082         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6083       }
6084     } else {
6085       if (perm) {
6086         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6087       } else {
6088         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6089       }
6090     }
6091   } else {
6092     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6093     if (clperm) {
6094       if (perm) {
6095         for (k = 0; k < dof; ++k) {
6096           if ((cind < cdof) && (k == cdofs[cind])) {
6097             ++cind;
6098             continue;
6099           }
6100           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6101         }
6102       } else {
6103         for (k = 0; k < dof; ++k) {
6104           if ((cind < cdof) && (k == cdofs[cind])) {
6105             ++cind;
6106             continue;
6107           }
6108           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6109         }
6110       }
6111     } else {
6112       if (perm) {
6113         for (k = 0; k < dof; ++k) {
6114           if ((cind < cdof) && (k == cdofs[cind])) {
6115             ++cind;
6116             continue;
6117           }
6118           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6119         }
6120       } else {
6121         for (k = 0; k < dof; ++k) {
6122           if ((cind < cdof) && (k == cdofs[cind])) {
6123             ++cind;
6124             continue;
6125           }
6126           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6127         }
6128       }
6129     }
6130   }
6131   PetscFunctionReturn(0);
6132 }
6133 
6134 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[])
6135 {
6136   PetscInt        cdof;  /* The number of constraints on this point */
6137   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6138   PetscScalar    *a;
6139   PetscInt        off, cind = 0, k;
6140 
6141   PetscFunctionBegin;
6142   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6143   PetscCall(PetscSectionGetOffset(section, point, &off));
6144   a = &array[off];
6145   if (cdof) {
6146     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6147     if (clperm) {
6148       if (perm) {
6149         for (k = 0; k < dof; ++k) {
6150           if ((cind < cdof) && (k == cdofs[cind])) {
6151             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6152             cind++;
6153           }
6154         }
6155       } else {
6156         for (k = 0; k < dof; ++k) {
6157           if ((cind < cdof) && (k == cdofs[cind])) {
6158             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6159             cind++;
6160           }
6161         }
6162       }
6163     } else {
6164       if (perm) {
6165         for (k = 0; k < dof; ++k) {
6166           if ((cind < cdof) && (k == cdofs[cind])) {
6167             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6168             cind++;
6169           }
6170         }
6171       } else {
6172         for (k = 0; k < dof; ++k) {
6173           if ((cind < cdof) && (k == cdofs[cind])) {
6174             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6175             cind++;
6176           }
6177         }
6178       }
6179     }
6180   }
6181   PetscFunctionReturn(0);
6182 }
6183 
6184 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[])
6185 {
6186   PetscScalar    *a;
6187   PetscInt        fdof, foff, fcdof, foffset = *offset;
6188   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6189   PetscInt        cind = 0, b;
6190 
6191   PetscFunctionBegin;
6192   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6193   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6194   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6195   a = &array[foff];
6196   if (!fcdof || setBC) {
6197     if (clperm) {
6198       if (perm) {
6199         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6200       } else {
6201         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6202       }
6203     } else {
6204       if (perm) {
6205         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6206       } else {
6207         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6208       }
6209     }
6210   } else {
6211     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6212     if (clperm) {
6213       if (perm) {
6214         for (b = 0; b < fdof; b++) {
6215           if ((cind < fcdof) && (b == fcdofs[cind])) {
6216             ++cind;
6217             continue;
6218           }
6219           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6220         }
6221       } else {
6222         for (b = 0; b < fdof; b++) {
6223           if ((cind < fcdof) && (b == fcdofs[cind])) {
6224             ++cind;
6225             continue;
6226           }
6227           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6228         }
6229       }
6230     } else {
6231       if (perm) {
6232         for (b = 0; b < fdof; b++) {
6233           if ((cind < fcdof) && (b == fcdofs[cind])) {
6234             ++cind;
6235             continue;
6236           }
6237           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6238         }
6239       } else {
6240         for (b = 0; b < fdof; b++) {
6241           if ((cind < fcdof) && (b == fcdofs[cind])) {
6242             ++cind;
6243             continue;
6244           }
6245           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6246         }
6247       }
6248     }
6249   }
6250   *offset += fdof;
6251   PetscFunctionReturn(0);
6252 }
6253 
6254 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[])
6255 {
6256   PetscScalar    *a;
6257   PetscInt        fdof, foff, fcdof, foffset = *offset;
6258   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6259   PetscInt        Nc, cind = 0, ncind = 0, b;
6260   PetscBool       ncSet, fcSet;
6261 
6262   PetscFunctionBegin;
6263   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6264   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6265   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6266   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6267   a = &array[foff];
6268   if (fcdof) {
6269     /* We just override fcdof and fcdofs with Ncc and comps */
6270     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6271     if (clperm) {
6272       if (perm) {
6273         if (comps) {
6274           for (b = 0; b < fdof; b++) {
6275             ncSet = fcSet = PETSC_FALSE;
6276             if (b % Nc == comps[ncind]) {
6277               ncind = (ncind + 1) % Ncc;
6278               ncSet = PETSC_TRUE;
6279             }
6280             if ((cind < fcdof) && (b == fcdofs[cind])) {
6281               ++cind;
6282               fcSet = PETSC_TRUE;
6283             }
6284             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6285           }
6286         } else {
6287           for (b = 0; b < fdof; b++) {
6288             if ((cind < fcdof) && (b == fcdofs[cind])) {
6289               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6290               ++cind;
6291             }
6292           }
6293         }
6294       } else {
6295         if (comps) {
6296           for (b = 0; b < fdof; b++) {
6297             ncSet = fcSet = PETSC_FALSE;
6298             if (b % Nc == comps[ncind]) {
6299               ncind = (ncind + 1) % Ncc;
6300               ncSet = PETSC_TRUE;
6301             }
6302             if ((cind < fcdof) && (b == fcdofs[cind])) {
6303               ++cind;
6304               fcSet = PETSC_TRUE;
6305             }
6306             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6307           }
6308         } else {
6309           for (b = 0; b < fdof; b++) {
6310             if ((cind < fcdof) && (b == fcdofs[cind])) {
6311               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6312               ++cind;
6313             }
6314           }
6315         }
6316       }
6317     } else {
6318       if (perm) {
6319         if (comps) {
6320           for (b = 0; b < fdof; b++) {
6321             ncSet = fcSet = PETSC_FALSE;
6322             if (b % Nc == comps[ncind]) {
6323               ncind = (ncind + 1) % Ncc;
6324               ncSet = PETSC_TRUE;
6325             }
6326             if ((cind < fcdof) && (b == fcdofs[cind])) {
6327               ++cind;
6328               fcSet = PETSC_TRUE;
6329             }
6330             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6331           }
6332         } else {
6333           for (b = 0; b < fdof; b++) {
6334             if ((cind < fcdof) && (b == fcdofs[cind])) {
6335               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6336               ++cind;
6337             }
6338           }
6339         }
6340       } else {
6341         if (comps) {
6342           for (b = 0; b < fdof; b++) {
6343             ncSet = fcSet = PETSC_FALSE;
6344             if (b % Nc == comps[ncind]) {
6345               ncind = (ncind + 1) % Ncc;
6346               ncSet = PETSC_TRUE;
6347             }
6348             if ((cind < fcdof) && (b == fcdofs[cind])) {
6349               ++cind;
6350               fcSet = PETSC_TRUE;
6351             }
6352             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6353           }
6354         } else {
6355           for (b = 0; b < fdof; b++) {
6356             if ((cind < fcdof) && (b == fcdofs[cind])) {
6357               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6358               ++cind;
6359             }
6360           }
6361         }
6362       }
6363     }
6364   }
6365   *offset += fdof;
6366   PetscFunctionReturn(0);
6367 }
6368 
6369 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6370 {
6371   PetscScalar    *array;
6372   const PetscInt *cone, *coneO;
6373   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6374 
6375   PetscFunctionBeginHot;
6376   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6377   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6378   PetscCall(DMPlexGetCone(dm, point, &cone));
6379   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6380   PetscCall(VecGetArray(v, &array));
6381   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6382     const PetscInt cp = !p ? point : cone[p - 1];
6383     const PetscInt o  = !p ? 0 : coneO[p - 1];
6384 
6385     if ((cp < pStart) || (cp >= pEnd)) {
6386       dof = 0;
6387       continue;
6388     }
6389     PetscCall(PetscSectionGetDof(section, cp, &dof));
6390     /* ADD_VALUES */
6391     {
6392       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6393       PetscScalar    *a;
6394       PetscInt        cdof, coff, cind = 0, k;
6395 
6396       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6397       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6398       a = &array[coff];
6399       if (!cdof) {
6400         if (o >= 0) {
6401           for (k = 0; k < dof; ++k) a[k] += values[off + k];
6402         } else {
6403           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
6404         }
6405       } else {
6406         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6407         if (o >= 0) {
6408           for (k = 0; k < dof; ++k) {
6409             if ((cind < cdof) && (k == cdofs[cind])) {
6410               ++cind;
6411               continue;
6412             }
6413             a[k] += values[off + k];
6414           }
6415         } else {
6416           for (k = 0; k < dof; ++k) {
6417             if ((cind < cdof) && (k == cdofs[cind])) {
6418               ++cind;
6419               continue;
6420             }
6421             a[k] += values[off + dof - k - 1];
6422           }
6423         }
6424       }
6425     }
6426   }
6427   PetscCall(VecRestoreArray(v, &array));
6428   PetscFunctionReturn(0);
6429 }
6430 
6431 /*@C
6432   DMPlexVecSetClosure - Set an array of the values on the closure of 'point'
6433 
6434   Not collective
6435 
6436   Input Parameters:
6437 + dm - The DM
6438 . section - The section describing the layout in v, or NULL to use the default section
6439 . v - The local vector
6440 . point - The point in the DM
6441 . values - The array of values
6442 - mode - The insert mode. One of INSERT_ALL_VALUES, ADD_ALL_VALUES, INSERT_VALUES, ADD_VALUES, INSERT_BC_VALUES, and ADD_BC_VALUES,
6443          where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions.
6444 
6445   Fortran Notes:
6446   This routine is only available in Fortran 90, and you must include petsc.h90 in your code.
6447 
6448   Level: intermediate
6449 
6450 .seealso `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6451 @*/
6452 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6453 {
6454   PetscSection    clSection;
6455   IS              clPoints;
6456   PetscScalar    *array;
6457   PetscInt       *points = NULL;
6458   const PetscInt *clp, *clperm = NULL;
6459   PetscInt        depth, numFields, numPoints, p, clsize;
6460 
6461   PetscFunctionBeginHot;
6462   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6463   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6464   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6465   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6466   PetscCall(DMPlexGetDepth(dm, &depth));
6467   PetscCall(PetscSectionGetNumFields(section, &numFields));
6468   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6469     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6470     PetscFunctionReturn(0);
6471   }
6472   /* Get points */
6473   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6474   for (clsize = 0, p = 0; p < numPoints; p++) {
6475     PetscInt dof;
6476     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6477     clsize += dof;
6478   }
6479   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
6480   /* Get array */
6481   PetscCall(VecGetArray(v, &array));
6482   /* Get values */
6483   if (numFields > 0) {
6484     PetscInt offset = 0, f;
6485     for (f = 0; f < numFields; ++f) {
6486       const PetscInt    **perms = NULL;
6487       const PetscScalar **flips = NULL;
6488 
6489       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6490       switch (mode) {
6491       case INSERT_VALUES:
6492         for (p = 0; p < numPoints; p++) {
6493           const PetscInt     point = points[2 * p];
6494           const PetscInt    *perm  = perms ? perms[p] : NULL;
6495           const PetscScalar *flip  = flips ? flips[p] : NULL;
6496           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array);
6497         }
6498         break;
6499       case INSERT_ALL_VALUES:
6500         for (p = 0; p < numPoints; p++) {
6501           const PetscInt     point = points[2 * p];
6502           const PetscInt    *perm  = perms ? perms[p] : NULL;
6503           const PetscScalar *flip  = flips ? flips[p] : NULL;
6504           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array);
6505         }
6506         break;
6507       case INSERT_BC_VALUES:
6508         for (p = 0; p < numPoints; p++) {
6509           const PetscInt     point = points[2 * p];
6510           const PetscInt    *perm  = perms ? perms[p] : NULL;
6511           const PetscScalar *flip  = flips ? flips[p] : NULL;
6512           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array);
6513         }
6514         break;
6515       case ADD_VALUES:
6516         for (p = 0; p < numPoints; p++) {
6517           const PetscInt     point = points[2 * p];
6518           const PetscInt    *perm  = perms ? perms[p] : NULL;
6519           const PetscScalar *flip  = flips ? flips[p] : NULL;
6520           updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array);
6521         }
6522         break;
6523       case ADD_ALL_VALUES:
6524         for (p = 0; p < numPoints; p++) {
6525           const PetscInt     point = points[2 * p];
6526           const PetscInt    *perm  = perms ? perms[p] : NULL;
6527           const PetscScalar *flip  = flips ? flips[p] : NULL;
6528           updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array);
6529         }
6530         break;
6531       case ADD_BC_VALUES:
6532         for (p = 0; p < numPoints; p++) {
6533           const PetscInt     point = points[2 * p];
6534           const PetscInt    *perm  = perms ? perms[p] : NULL;
6535           const PetscScalar *flip  = flips ? flips[p] : NULL;
6536           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array);
6537         }
6538         break;
6539       default:
6540         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6541       }
6542       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6543     }
6544   } else {
6545     PetscInt            dof, off;
6546     const PetscInt    **perms = NULL;
6547     const PetscScalar **flips = NULL;
6548 
6549     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6550     switch (mode) {
6551     case INSERT_VALUES:
6552       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6553         const PetscInt     point = points[2 * p];
6554         const PetscInt    *perm  = perms ? perms[p] : NULL;
6555         const PetscScalar *flip  = flips ? flips[p] : NULL;
6556         PetscCall(PetscSectionGetDof(section, point, &dof));
6557         updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array);
6558       }
6559       break;
6560     case INSERT_ALL_VALUES:
6561       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6562         const PetscInt     point = points[2 * p];
6563         const PetscInt    *perm  = perms ? perms[p] : NULL;
6564         const PetscScalar *flip  = flips ? flips[p] : NULL;
6565         PetscCall(PetscSectionGetDof(section, point, &dof));
6566         updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array);
6567       }
6568       break;
6569     case INSERT_BC_VALUES:
6570       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6571         const PetscInt     point = points[2 * p];
6572         const PetscInt    *perm  = perms ? perms[p] : NULL;
6573         const PetscScalar *flip  = flips ? flips[p] : NULL;
6574         PetscCall(PetscSectionGetDof(section, point, &dof));
6575         updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array);
6576       }
6577       break;
6578     case ADD_VALUES:
6579       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6580         const PetscInt     point = points[2 * p];
6581         const PetscInt    *perm  = perms ? perms[p] : NULL;
6582         const PetscScalar *flip  = flips ? flips[p] : NULL;
6583         PetscCall(PetscSectionGetDof(section, point, &dof));
6584         updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array);
6585       }
6586       break;
6587     case ADD_ALL_VALUES:
6588       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6589         const PetscInt     point = points[2 * p];
6590         const PetscInt    *perm  = perms ? perms[p] : NULL;
6591         const PetscScalar *flip  = flips ? flips[p] : NULL;
6592         PetscCall(PetscSectionGetDof(section, point, &dof));
6593         updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array);
6594       }
6595       break;
6596     case ADD_BC_VALUES:
6597       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6598         const PetscInt     point = points[2 * p];
6599         const PetscInt    *perm  = perms ? perms[p] : NULL;
6600         const PetscScalar *flip  = flips ? flips[p] : NULL;
6601         PetscCall(PetscSectionGetDof(section, point, &dof));
6602         updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array);
6603       }
6604       break;
6605     default:
6606       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6607     }
6608     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6609   }
6610   /* Cleanup points */
6611   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6612   /* Cleanup array */
6613   PetscCall(VecRestoreArray(v, &array));
6614   PetscFunctionReturn(0);
6615 }
6616 
6617 /* Check whether the given point is in the label. If not, update the offset to skip this point */
6618 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
6619 {
6620   PetscFunctionBegin;
6621   *contains = PETSC_TRUE;
6622   if (label) {
6623     PetscInt fdof;
6624 
6625     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
6626     if (!*contains) {
6627       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6628       *offset += fdof;
6629       PetscFunctionReturn(0);
6630     }
6631   }
6632   PetscFunctionReturn(0);
6633 }
6634 
6635 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
6636 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)
6637 {
6638   PetscSection    clSection;
6639   IS              clPoints;
6640   PetscScalar    *array;
6641   PetscInt       *points = NULL;
6642   const PetscInt *clp;
6643   PetscInt        numFields, numPoints, p;
6644   PetscInt        offset = 0, f;
6645 
6646   PetscFunctionBeginHot;
6647   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6648   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6649   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6650   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6651   PetscCall(PetscSectionGetNumFields(section, &numFields));
6652   /* Get points */
6653   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6654   /* Get array */
6655   PetscCall(VecGetArray(v, &array));
6656   /* Get values */
6657   for (f = 0; f < numFields; ++f) {
6658     const PetscInt    **perms = NULL;
6659     const PetscScalar **flips = NULL;
6660     PetscBool           contains;
6661 
6662     if (!fieldActive[f]) {
6663       for (p = 0; p < numPoints * 2; p += 2) {
6664         PetscInt fdof;
6665         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
6666         offset += fdof;
6667       }
6668       continue;
6669     }
6670     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6671     switch (mode) {
6672     case INSERT_VALUES:
6673       for (p = 0; p < numPoints; p++) {
6674         const PetscInt     point = points[2 * p];
6675         const PetscInt    *perm  = perms ? perms[p] : NULL;
6676         const PetscScalar *flip  = flips ? flips[p] : NULL;
6677         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6678         if (!contains) continue;
6679         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
6680       }
6681       break;
6682     case INSERT_ALL_VALUES:
6683       for (p = 0; p < numPoints; p++) {
6684         const PetscInt     point = points[2 * p];
6685         const PetscInt    *perm  = perms ? perms[p] : NULL;
6686         const PetscScalar *flip  = flips ? flips[p] : NULL;
6687         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6688         if (!contains) continue;
6689         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
6690       }
6691       break;
6692     case INSERT_BC_VALUES:
6693       for (p = 0; p < numPoints; p++) {
6694         const PetscInt     point = points[2 * p];
6695         const PetscInt    *perm  = perms ? perms[p] : NULL;
6696         const PetscScalar *flip  = flips ? flips[p] : NULL;
6697         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6698         if (!contains) continue;
6699         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
6700       }
6701       break;
6702     case ADD_VALUES:
6703       for (p = 0; p < numPoints; p++) {
6704         const PetscInt     point = points[2 * p];
6705         const PetscInt    *perm  = perms ? perms[p] : NULL;
6706         const PetscScalar *flip  = flips ? flips[p] : NULL;
6707         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6708         if (!contains) continue;
6709         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
6710       }
6711       break;
6712     case ADD_ALL_VALUES:
6713       for (p = 0; p < numPoints; p++) {
6714         const PetscInt     point = points[2 * p];
6715         const PetscInt    *perm  = perms ? perms[p] : NULL;
6716         const PetscScalar *flip  = flips ? flips[p] : NULL;
6717         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6718         if (!contains) continue;
6719         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
6720       }
6721       break;
6722     default:
6723       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6724     }
6725     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6726   }
6727   /* Cleanup points */
6728   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6729   /* Cleanup array */
6730   PetscCall(VecRestoreArray(v, &array));
6731   PetscFunctionReturn(0);
6732 }
6733 
6734 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
6735 {
6736   PetscMPIInt rank;
6737   PetscInt    i, j;
6738 
6739   PetscFunctionBegin;
6740   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
6741   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
6742   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
6743   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
6744   numCIndices = numCIndices ? numCIndices : numRIndices;
6745   if (!values) PetscFunctionReturn(0);
6746   for (i = 0; i < numRIndices; i++) {
6747     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
6748     for (j = 0; j < numCIndices; j++) {
6749 #if defined(PETSC_USE_COMPLEX)
6750       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
6751 #else
6752       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
6753 #endif
6754     }
6755     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
6756   }
6757   PetscFunctionReturn(0);
6758 }
6759 
6760 /*
6761   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
6762 
6763   Input Parameters:
6764 + section - The section for this data layout
6765 . islocal - Is the section (and thus indices being requested) local or global?
6766 . point   - The point contributing dofs with these indices
6767 . off     - The global offset of this point
6768 . loff    - The local offset of each field
6769 . setBC   - The flag determining whether to include indices of boundary values
6770 . perm    - A permutation of the dofs on this point, or NULL
6771 - indperm - A permutation of the entire indices array, or NULL
6772 
6773   Output Parameter:
6774 . indices - Indices for dofs on this point
6775 
6776   Level: developer
6777 
6778   Note: The indices could be local or global, depending on the value of 'off'.
6779 */
6780 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
6781 {
6782   PetscInt        dof;   /* The number of unknowns on this point */
6783   PetscInt        cdof;  /* The number of constraints on this point */
6784   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6785   PetscInt        cind = 0, k;
6786 
6787   PetscFunctionBegin;
6788   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
6789   PetscCall(PetscSectionGetDof(section, point, &dof));
6790   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6791   if (!cdof || setBC) {
6792     for (k = 0; k < dof; ++k) {
6793       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
6794       const PetscInt ind    = indperm ? indperm[preind] : preind;
6795 
6796       indices[ind] = off + k;
6797     }
6798   } else {
6799     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6800     for (k = 0; k < dof; ++k) {
6801       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
6802       const PetscInt ind    = indperm ? indperm[preind] : preind;
6803 
6804       if ((cind < cdof) && (k == cdofs[cind])) {
6805         /* Insert check for returning constrained indices */
6806         indices[ind] = -(off + k + 1);
6807         ++cind;
6808       } else {
6809         indices[ind] = off + k - (islocal ? 0 : cind);
6810       }
6811     }
6812   }
6813   *loff += dof;
6814   PetscFunctionReturn(0);
6815 }
6816 
6817 /*
6818  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
6819 
6820  Input Parameters:
6821 + section - a section (global or local)
6822 - islocal - PETSC_TRUE if requesting local indices (i.e., section is local); PETSC_FALSE for global
6823 . point - point within section
6824 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
6825 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
6826 . setBC - identify constrained (boundary condition) points via involution.
6827 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
6828 . permsoff - offset
6829 - indperm - index permutation
6830 
6831  Output Parameter:
6832 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
6833 . indices - array to hold indices (as defined by section) of each dof associated with point
6834 
6835  Notes:
6836  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
6837  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
6838  in the local vector.
6839 
6840  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
6841  significant).  It is invalid to call with a global section and setBC=true.
6842 
6843  Developer Note:
6844  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
6845  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
6846  offset could be obtained from the section instead of passing it explicitly as we do now.
6847 
6848  Example:
6849  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
6850  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
6851  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
6852  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.
6853 
6854  Level: developer
6855 */
6856 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[])
6857 {
6858   PetscInt numFields, foff, f;
6859 
6860   PetscFunctionBegin;
6861   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
6862   PetscCall(PetscSectionGetNumFields(section, &numFields));
6863   for (f = 0, foff = 0; f < numFields; ++f) {
6864     PetscInt        fdof, cfdof;
6865     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6866     PetscInt        cind = 0, b;
6867     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6868 
6869     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6870     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
6871     if (!cfdof || setBC) {
6872       for (b = 0; b < fdof; ++b) {
6873         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
6874         const PetscInt ind    = indperm ? indperm[preind] : preind;
6875 
6876         indices[ind] = off + foff + b;
6877       }
6878     } else {
6879       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6880       for (b = 0; b < fdof; ++b) {
6881         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
6882         const PetscInt ind    = indperm ? indperm[preind] : preind;
6883 
6884         if ((cind < cfdof) && (b == fcdofs[cind])) {
6885           indices[ind] = -(off + foff + b + 1);
6886           ++cind;
6887         } else {
6888           indices[ind] = off + foff + b - (islocal ? 0 : cind);
6889         }
6890       }
6891     }
6892     foff += (setBC || islocal ? fdof : (fdof - cfdof));
6893     foffs[f] += fdof;
6894   }
6895   PetscFunctionReturn(0);
6896 }
6897 
6898 /*
6899   This version believes the globalSection offsets for each field, rather than just the point offset
6900 
6901  . foffs - The offset into 'indices' for each field, since it is segregated by field
6902 
6903  Notes:
6904  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
6905  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
6906 */
6907 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
6908 {
6909   PetscInt numFields, foff, f;
6910 
6911   PetscFunctionBegin;
6912   PetscCall(PetscSectionGetNumFields(section, &numFields));
6913   for (f = 0; f < numFields; ++f) {
6914     PetscInt        fdof, cfdof;
6915     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6916     PetscInt        cind = 0, b;
6917     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6918 
6919     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6920     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
6921     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
6922     if (!cfdof) {
6923       for (b = 0; b < fdof; ++b) {
6924         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
6925         const PetscInt ind    = indperm ? indperm[preind] : preind;
6926 
6927         indices[ind] = foff + b;
6928       }
6929     } else {
6930       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6931       for (b = 0; b < fdof; ++b) {
6932         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
6933         const PetscInt ind    = indperm ? indperm[preind] : preind;
6934 
6935         if ((cind < cfdof) && (b == fcdofs[cind])) {
6936           indices[ind] = -(foff + b + 1);
6937           ++cind;
6938         } else {
6939           indices[ind] = foff + b - cind;
6940         }
6941       }
6942     }
6943     foffs[f] += fdof;
6944   }
6945   PetscFunctionReturn(0);
6946 }
6947 
6948 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)
6949 {
6950   Mat             cMat;
6951   PetscSection    aSec, cSec;
6952   IS              aIS;
6953   PetscInt        aStart = -1, aEnd = -1;
6954   const PetscInt *anchors;
6955   PetscInt        numFields, f, p, q, newP = 0;
6956   PetscInt        newNumPoints = 0, newNumIndices = 0;
6957   PetscInt       *newPoints, *indices, *newIndices;
6958   PetscInt        maxAnchor, maxDof;
6959   PetscInt        newOffsets[32];
6960   PetscInt       *pointMatOffsets[32];
6961   PetscInt       *newPointOffsets[32];
6962   PetscScalar    *pointMat[32];
6963   PetscScalar    *newValues      = NULL, *tmpValues;
6964   PetscBool       anyConstrained = PETSC_FALSE;
6965 
6966   PetscFunctionBegin;
6967   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6968   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6969   PetscCall(PetscSectionGetNumFields(section, &numFields));
6970 
6971   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
6972   /* if there are point-to-point constraints */
6973   if (aSec) {
6974     PetscCall(PetscArrayzero(newOffsets, 32));
6975     PetscCall(ISGetIndices(aIS, &anchors));
6976     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
6977     /* figure out how many points are going to be in the new element matrix
6978      * (we allow double counting, because it's all just going to be summed
6979      * into the global matrix anyway) */
6980     for (p = 0; p < 2 * numPoints; p += 2) {
6981       PetscInt b    = points[p];
6982       PetscInt bDof = 0, bSecDof;
6983 
6984       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
6985       if (!bSecDof) continue;
6986       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6987       if (bDof) {
6988         /* this point is constrained */
6989         /* it is going to be replaced by its anchors */
6990         PetscInt bOff, q;
6991 
6992         anyConstrained = PETSC_TRUE;
6993         newNumPoints += bDof;
6994         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6995         for (q = 0; q < bDof; q++) {
6996           PetscInt a = anchors[bOff + q];
6997           PetscInt aDof;
6998 
6999           PetscCall(PetscSectionGetDof(section, a, &aDof));
7000           newNumIndices += aDof;
7001           for (f = 0; f < numFields; ++f) {
7002             PetscInt fDof;
7003 
7004             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7005             newOffsets[f + 1] += fDof;
7006           }
7007         }
7008       } else {
7009         /* this point is not constrained */
7010         newNumPoints++;
7011         newNumIndices += bSecDof;
7012         for (f = 0; f < numFields; ++f) {
7013           PetscInt fDof;
7014 
7015           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7016           newOffsets[f + 1] += fDof;
7017         }
7018       }
7019     }
7020   }
7021   if (!anyConstrained) {
7022     if (outNumPoints) *outNumPoints = 0;
7023     if (outNumIndices) *outNumIndices = 0;
7024     if (outPoints) *outPoints = NULL;
7025     if (outValues) *outValues = NULL;
7026     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7027     PetscFunctionReturn(0);
7028   }
7029 
7030   if (outNumPoints) *outNumPoints = newNumPoints;
7031   if (outNumIndices) *outNumIndices = newNumIndices;
7032 
7033   for (f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7034 
7035   if (!outPoints && !outValues) {
7036     if (offsets) {
7037       for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7038     }
7039     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7040     PetscFunctionReturn(0);
7041   }
7042 
7043   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7044 
7045   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7046 
7047   /* workspaces */
7048   if (numFields) {
7049     for (f = 0; f < numFields; f++) {
7050       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7051       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7052     }
7053   } else {
7054     PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7055     PetscCall(DMGetWorkArray(dm, numPoints, MPIU_INT, &newPointOffsets[0]));
7056   }
7057 
7058   /* get workspaces for the point-to-point matrices */
7059   if (numFields) {
7060     PetscInt totalOffset, totalMatOffset;
7061 
7062     for (p = 0; p < numPoints; p++) {
7063       PetscInt b    = points[2 * p];
7064       PetscInt bDof = 0, bSecDof;
7065 
7066       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7067       if (!bSecDof) {
7068         for (f = 0; f < numFields; f++) {
7069           newPointOffsets[f][p + 1] = 0;
7070           pointMatOffsets[f][p + 1] = 0;
7071         }
7072         continue;
7073       }
7074       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7075       if (bDof) {
7076         for (f = 0; f < numFields; f++) {
7077           PetscInt fDof, q, bOff, allFDof = 0;
7078 
7079           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7080           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7081           for (q = 0; q < bDof; q++) {
7082             PetscInt a = anchors[bOff + q];
7083             PetscInt aFDof;
7084 
7085             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
7086             allFDof += aFDof;
7087           }
7088           newPointOffsets[f][p + 1] = allFDof;
7089           pointMatOffsets[f][p + 1] = fDof * allFDof;
7090         }
7091       } else {
7092         for (f = 0; f < numFields; f++) {
7093           PetscInt fDof;
7094 
7095           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7096           newPointOffsets[f][p + 1] = fDof;
7097           pointMatOffsets[f][p + 1] = 0;
7098         }
7099       }
7100     }
7101     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
7102       newPointOffsets[f][0] = totalOffset;
7103       pointMatOffsets[f][0] = totalMatOffset;
7104       for (p = 0; p < numPoints; p++) {
7105         newPointOffsets[f][p + 1] += newPointOffsets[f][p];
7106         pointMatOffsets[f][p + 1] += pointMatOffsets[f][p];
7107       }
7108       totalOffset    = newPointOffsets[f][numPoints];
7109       totalMatOffset = pointMatOffsets[f][numPoints];
7110       PetscCall(DMGetWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7111     }
7112   } else {
7113     for (p = 0; p < numPoints; p++) {
7114       PetscInt b    = points[2 * p];
7115       PetscInt bDof = 0, bSecDof;
7116 
7117       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7118       if (!bSecDof) {
7119         newPointOffsets[0][p + 1] = 0;
7120         pointMatOffsets[0][p + 1] = 0;
7121         continue;
7122       }
7123       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7124       if (bDof) {
7125         PetscInt bOff, q, allDof = 0;
7126 
7127         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7128         for (q = 0; q < bDof; q++) {
7129           PetscInt a = anchors[bOff + q], aDof;
7130 
7131           PetscCall(PetscSectionGetDof(section, a, &aDof));
7132           allDof += aDof;
7133         }
7134         newPointOffsets[0][p + 1] = allDof;
7135         pointMatOffsets[0][p + 1] = bSecDof * allDof;
7136       } else {
7137         newPointOffsets[0][p + 1] = bSecDof;
7138         pointMatOffsets[0][p + 1] = 0;
7139       }
7140     }
7141     newPointOffsets[0][0] = 0;
7142     pointMatOffsets[0][0] = 0;
7143     for (p = 0; p < numPoints; p++) {
7144       newPointOffsets[0][p + 1] += newPointOffsets[0][p];
7145       pointMatOffsets[0][p + 1] += pointMatOffsets[0][p];
7146     }
7147     PetscCall(DMGetWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7148   }
7149 
7150   /* output arrays */
7151   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7152 
7153   /* get the point-to-point matrices; construct newPoints */
7154   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
7155   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
7156   PetscCall(DMGetWorkArray(dm, maxDof, MPIU_INT, &indices));
7157   PetscCall(DMGetWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7158   if (numFields) {
7159     for (p = 0, newP = 0; p < numPoints; p++) {
7160       PetscInt b    = points[2 * p];
7161       PetscInt o    = points[2 * p + 1];
7162       PetscInt bDof = 0, bSecDof;
7163 
7164       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7165       if (!bSecDof) continue;
7166       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7167       if (bDof) {
7168         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
7169 
7170         fStart[0] = 0;
7171         fEnd[0]   = 0;
7172         for (f = 0; f < numFields; f++) {
7173           PetscInt fDof;
7174 
7175           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
7176           fStart[f + 1] = fStart[f] + fDof;
7177           fEnd[f + 1]   = fStart[f + 1];
7178         }
7179         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7180         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7181 
7182         fAnchorStart[0] = 0;
7183         fAnchorEnd[0]   = 0;
7184         for (f = 0; f < numFields; f++) {
7185           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7186 
7187           fAnchorStart[f + 1] = fAnchorStart[f] + fDof;
7188           fAnchorEnd[f + 1]   = fAnchorStart[f + 1];
7189         }
7190         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7191         for (q = 0; q < bDof; q++) {
7192           PetscInt a = anchors[bOff + q], aOff;
7193 
7194           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7195           newPoints[2 * (newP + q)]     = a;
7196           newPoints[2 * (newP + q) + 1] = 0;
7197           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7198           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7199         }
7200         newP += bDof;
7201 
7202         if (outValues) {
7203           /* get the point-to-point submatrix */
7204           for (f = 0; f < numFields; f++) PetscCall(MatGetValues(cMat, fEnd[f] - fStart[f], indices + fStart[f], fAnchorEnd[f] - fAnchorStart[f], newIndices + fAnchorStart[f], pointMat[f] + pointMatOffsets[f][p]));
7205         }
7206       } else {
7207         newPoints[2 * newP]     = b;
7208         newPoints[2 * newP + 1] = o;
7209         newP++;
7210       }
7211     }
7212   } else {
7213     for (p = 0; p < numPoints; p++) {
7214       PetscInt b    = points[2 * p];
7215       PetscInt o    = points[2 * p + 1];
7216       PetscInt bDof = 0, bSecDof;
7217 
7218       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7219       if (!bSecDof) continue;
7220       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7221       if (bDof) {
7222         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7223 
7224         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7225         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7226 
7227         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7228         for (q = 0; q < bDof; q++) {
7229           PetscInt a = anchors[bOff + q], aOff;
7230 
7231           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7232 
7233           newPoints[2 * (newP + q)]     = a;
7234           newPoints[2 * (newP + q) + 1] = 0;
7235           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7236           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7237         }
7238         newP += bDof;
7239 
7240         /* get the point-to-point submatrix */
7241         if (outValues) PetscCall(MatGetValues(cMat, bEnd, indices, bAnchorEnd, newIndices, pointMat[0] + pointMatOffsets[0][p]));
7242       } else {
7243         newPoints[2 * newP]     = b;
7244         newPoints[2 * newP + 1] = o;
7245         newP++;
7246       }
7247     }
7248   }
7249 
7250   if (outValues) {
7251     PetscCall(DMGetWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7252     PetscCall(PetscArrayzero(tmpValues, newNumIndices * numIndices));
7253     /* multiply constraints on the right */
7254     if (numFields) {
7255       for (f = 0; f < numFields; f++) {
7256         PetscInt oldOff = offsets[f];
7257 
7258         for (p = 0; p < numPoints; p++) {
7259           PetscInt cStart = newPointOffsets[f][p];
7260           PetscInt b      = points[2 * p];
7261           PetscInt c, r, k;
7262           PetscInt dof;
7263 
7264           PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7265           if (!dof) continue;
7266           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7267             PetscInt           nCols = newPointOffsets[f][p + 1] - cStart;
7268             const PetscScalar *mat   = pointMat[f] + pointMatOffsets[f][p];
7269 
7270             for (r = 0; r < numIndices; r++) {
7271               for (c = 0; c < nCols; c++) {
7272                 for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7273               }
7274             }
7275           } else {
7276             /* copy this column as is */
7277             for (r = 0; r < numIndices; r++) {
7278               for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7279             }
7280           }
7281           oldOff += dof;
7282         }
7283       }
7284     } else {
7285       PetscInt oldOff = 0;
7286       for (p = 0; p < numPoints; p++) {
7287         PetscInt cStart = newPointOffsets[0][p];
7288         PetscInt b      = points[2 * p];
7289         PetscInt c, r, k;
7290         PetscInt dof;
7291 
7292         PetscCall(PetscSectionGetDof(section, b, &dof));
7293         if (!dof) continue;
7294         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7295           PetscInt           nCols = newPointOffsets[0][p + 1] - cStart;
7296           const PetscScalar *mat   = pointMat[0] + pointMatOffsets[0][p];
7297 
7298           for (r = 0; r < numIndices; r++) {
7299             for (c = 0; c < nCols; c++) {
7300               for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7301             }
7302           }
7303         } else {
7304           /* copy this column as is */
7305           for (r = 0; r < numIndices; r++) {
7306             for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7307           }
7308         }
7309         oldOff += dof;
7310       }
7311     }
7312 
7313     if (multiplyLeft) {
7314       PetscCall(DMGetWorkArray(dm, newNumIndices * newNumIndices, MPIU_SCALAR, &newValues));
7315       PetscCall(PetscArrayzero(newValues, newNumIndices * newNumIndices));
7316       /* multiply constraints transpose on the left */
7317       if (numFields) {
7318         for (f = 0; f < numFields; f++) {
7319           PetscInt oldOff = offsets[f];
7320 
7321           for (p = 0; p < numPoints; p++) {
7322             PetscInt rStart = newPointOffsets[f][p];
7323             PetscInt b      = points[2 * p];
7324             PetscInt c, r, k;
7325             PetscInt dof;
7326 
7327             PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7328             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7329               PetscInt                          nRows = newPointOffsets[f][p + 1] - rStart;
7330               const PetscScalar *PETSC_RESTRICT mat   = pointMat[f] + pointMatOffsets[f][p];
7331 
7332               for (r = 0; r < nRows; r++) {
7333                 for (c = 0; c < newNumIndices; c++) {
7334                   for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7335                 }
7336               }
7337             } else {
7338               /* copy this row as is */
7339               for (r = 0; r < dof; r++) {
7340                 for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7341               }
7342             }
7343             oldOff += dof;
7344           }
7345         }
7346       } else {
7347         PetscInt oldOff = 0;
7348 
7349         for (p = 0; p < numPoints; p++) {
7350           PetscInt rStart = newPointOffsets[0][p];
7351           PetscInt b      = points[2 * p];
7352           PetscInt c, r, k;
7353           PetscInt dof;
7354 
7355           PetscCall(PetscSectionGetDof(section, b, &dof));
7356           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7357             PetscInt                          nRows = newPointOffsets[0][p + 1] - rStart;
7358             const PetscScalar *PETSC_RESTRICT mat   = pointMat[0] + pointMatOffsets[0][p];
7359 
7360             for (r = 0; r < nRows; r++) {
7361               for (c = 0; c < newNumIndices; c++) {
7362                 for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7363               }
7364             }
7365           } else {
7366             /* copy this row as is */
7367             for (r = 0; r < dof; r++) {
7368               for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7369             }
7370           }
7371           oldOff += dof;
7372         }
7373       }
7374 
7375       PetscCall(DMRestoreWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7376     } else {
7377       newValues = tmpValues;
7378     }
7379   }
7380 
7381   /* clean up */
7382   PetscCall(DMRestoreWorkArray(dm, maxDof, MPIU_INT, &indices));
7383   PetscCall(DMRestoreWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7384 
7385   if (numFields) {
7386     for (f = 0; f < numFields; f++) {
7387       PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7388       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7389       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7390     }
7391   } else {
7392     PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7393     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7394     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[0]));
7395   }
7396   PetscCall(ISRestoreIndices(aIS, &anchors));
7397 
7398   /* output */
7399   if (outPoints) {
7400     *outPoints = newPoints;
7401   } else {
7402     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7403   }
7404   if (outValues) *outValues = newValues;
7405   for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7406   PetscFunctionReturn(0);
7407 }
7408 
7409 /*@C
7410   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7411 
7412   Not collective
7413 
7414   Input Parameters:
7415 + dm         - The DM
7416 . section    - The PetscSection describing the points (a local section)
7417 . idxSection - The PetscSection from which to obtain indices (may be local or global)
7418 . point      - The point defining the closure
7419 - useClPerm  - Use the closure point permutation if available
7420 
7421   Output Parameters:
7422 + numIndices - The number of dof indices in the closure of point with the input sections
7423 . indices    - The dof indices
7424 . outOffsets - Array to write the field offsets into, or NULL
7425 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7426 
7427   Notes:
7428   Must call DMPlexRestoreClosureIndices() to free allocated memory
7429 
7430   If idxSection is global, any constrained dofs (see DMAddBoundary(), for example) will get negative indices.  The value
7431   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7432   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7433   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7434   indices (with the above semantics) are implied.
7435 
7436   Level: advanced
7437 
7438 .seealso `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7439 @*/
7440 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7441 {
7442   /* Closure ordering */
7443   PetscSection    clSection;
7444   IS              clPoints;
7445   const PetscInt *clp;
7446   PetscInt       *points;
7447   const PetscInt *clperm = NULL;
7448   /* Dof permutation and sign flips */
7449   const PetscInt    **perms[32] = {NULL};
7450   const PetscScalar **flips[32] = {NULL};
7451   PetscScalar        *valCopy   = NULL;
7452   /* Hanging node constraints */
7453   PetscInt    *pointsC = NULL;
7454   PetscScalar *valuesC = NULL;
7455   PetscInt     NclC, NiC;
7456 
7457   PetscInt *idx;
7458   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7459   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7460 
7461   PetscFunctionBeginHot;
7462   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7463   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7464   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7465   if (numIndices) PetscValidIntPointer(numIndices, 6);
7466   if (indices) PetscValidPointer(indices, 7);
7467   if (outOffsets) PetscValidIntPointer(outOffsets, 8);
7468   if (values) PetscValidPointer(values, 9);
7469   PetscCall(PetscSectionGetNumFields(section, &Nf));
7470   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7471   PetscCall(PetscArrayzero(offsets, 32));
7472   /* 1) Get points in closure */
7473   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7474   if (useClPerm) {
7475     PetscInt depth, clsize;
7476     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7477     for (clsize = 0, p = 0; p < Ncl; p++) {
7478       PetscInt dof;
7479       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7480       clsize += dof;
7481     }
7482     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7483   }
7484   /* 2) Get number of indices on these points and field offsets from section */
7485   for (p = 0; p < Ncl * 2; p += 2) {
7486     PetscInt dof, fdof;
7487 
7488     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7489     for (f = 0; f < Nf; ++f) {
7490       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7491       offsets[f + 1] += fdof;
7492     }
7493     Ni += dof;
7494   }
7495   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7496   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7497   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7498   for (f = 0; f < PetscMax(1, Nf); ++f) {
7499     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7500     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7501     /* may need to apply sign changes to the element matrix */
7502     if (values && flips[f]) {
7503       PetscInt foffset = offsets[f];
7504 
7505       for (p = 0; p < Ncl; ++p) {
7506         PetscInt           pnt  = points[2 * p], fdof;
7507         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7508 
7509         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7510         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7511         if (flip) {
7512           PetscInt i, j, k;
7513 
7514           if (!valCopy) {
7515             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7516             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7517             *values = valCopy;
7518           }
7519           for (i = 0; i < fdof; ++i) {
7520             PetscScalar fval = flip[i];
7521 
7522             for (k = 0; k < Ni; ++k) {
7523               valCopy[Ni * (foffset + i) + k] *= fval;
7524               valCopy[Ni * k + (foffset + i)] *= fval;
7525             }
7526           }
7527         }
7528         foffset += fdof;
7529       }
7530     }
7531   }
7532   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7533   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7534   if (NclC) {
7535     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7536     for (f = 0; f < PetscMax(1, Nf); ++f) {
7537       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7538       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7539     }
7540     for (f = 0; f < PetscMax(1, Nf); ++f) {
7541       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7542       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7543     }
7544     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7545     Ncl    = NclC;
7546     Ni     = NiC;
7547     points = pointsC;
7548     if (values) *values = valuesC;
7549   }
7550   /* 5) Calculate indices */
7551   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7552   if (Nf) {
7553     PetscInt  idxOff;
7554     PetscBool useFieldOffsets;
7555 
7556     if (outOffsets) {
7557       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
7558     }
7559     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7560     if (useFieldOffsets) {
7561       for (p = 0; p < Ncl; ++p) {
7562         const PetscInt pnt = points[p * 2];
7563 
7564         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7565       }
7566     } else {
7567       for (p = 0; p < Ncl; ++p) {
7568         const PetscInt pnt = points[p * 2];
7569 
7570         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7571         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7572          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7573          * global section. */
7574         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7575       }
7576     }
7577   } else {
7578     PetscInt off = 0, idxOff;
7579 
7580     for (p = 0; p < Ncl; ++p) {
7581       const PetscInt  pnt  = points[p * 2];
7582       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7583 
7584       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7585       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7586        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7587       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7588     }
7589   }
7590   /* 6) Cleanup */
7591   for (f = 0; f < PetscMax(1, Nf); ++f) {
7592     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7593     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7594   }
7595   if (NclC) {
7596     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
7597   } else {
7598     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7599   }
7600 
7601   if (numIndices) *numIndices = Ni;
7602   if (indices) *indices = idx;
7603   PetscFunctionReturn(0);
7604 }
7605 
7606 /*@C
7607   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7608 
7609   Not collective
7610 
7611   Input Parameters:
7612 + dm         - The DM
7613 . section    - The PetscSection describing the points (a local section)
7614 . idxSection - The PetscSection from which to obtain indices (may be local or global)
7615 . point      - The point defining the closure
7616 - useClPerm  - Use the closure point permutation if available
7617 
7618   Output Parameters:
7619 + numIndices - The number of dof indices in the closure of point with the input sections
7620 . indices    - The dof indices
7621 . outOffsets - Array to write the field offsets into, or NULL
7622 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7623 
7624   Notes:
7625   If values were modified, the user is responsible for calling DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values).
7626 
7627   If idxSection is global, any constrained dofs (see DMAddBoundary(), for example) will get negative indices.  The value
7628   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7629   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7630   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7631   indices (with the above semantics) are implied.
7632 
7633   Level: advanced
7634 
7635 .seealso `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7636 @*/
7637 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7638 {
7639   PetscFunctionBegin;
7640   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7641   PetscValidPointer(indices, 7);
7642   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
7643   PetscFunctionReturn(0);
7644 }
7645 
7646 /*@C
7647   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
7648 
7649   Not collective
7650 
7651   Input Parameters:
7652 + dm - The DM
7653 . section - The section describing the layout in v, or NULL to use the default section
7654 . globalSection - The section describing the layout in v, or NULL to use the default global section
7655 . A - The matrix
7656 . point - The point in the DM
7657 . values - The array of values
7658 - mode - The insert mode, where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions
7659 
7660   Fortran Notes:
7661   This routine is only available in Fortran 90, and you must include petsc.h90 in your code.
7662 
7663   Level: intermediate
7664 
7665 .seealso `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7666 @*/
7667 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7668 {
7669   DM_Plex           *mesh = (DM_Plex *)dm->data;
7670   PetscInt          *indices;
7671   PetscInt           numIndices;
7672   const PetscScalar *valuesOrig = values;
7673   PetscErrorCode     ierr;
7674 
7675   PetscFunctionBegin;
7676   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7677   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7678   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7679   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
7680   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
7681   PetscValidHeaderSpecific(A, MAT_CLASSID, 4);
7682 
7683   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7684 
7685   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
7686   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7687   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
7688   if (ierr) {
7689     PetscMPIInt rank;
7690 
7691     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7692     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7693     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
7694     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7695     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7696     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
7697   }
7698   if (mesh->printFEM > 1) {
7699     PetscInt i;
7700     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
7701     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
7702     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
7703   }
7704 
7705   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7706   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7707   PetscFunctionReturn(0);
7708 }
7709 
7710 /*@C
7711   DMPlexMatSetClosure - Set an array of the values on the closure of 'point' using a different row and column section
7712 
7713   Not collective
7714 
7715   Input Parameters:
7716 + dmRow - The DM for the row fields
7717 . sectionRow - The section describing the layout, or NULL to use the default section in dmRow
7718 . globalSectionRow - The section describing the layout, or NULL to use the default global section in dmRow
7719 . dmCol - The DM for the column fields
7720 . sectionCol - The section describing the layout, or NULL to use the default section in dmCol
7721 . globalSectionCol - The section describing the layout, or NULL to use the default global section in dmCol
7722 . A - The matrix
7723 . point - The point in the DMs
7724 . values - The array of values
7725 - mode - The insert mode, where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions
7726 
7727   Level: intermediate
7728 
7729 .seealso `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7730 @*/
7731 PetscErrorCode DMPlexMatSetClosureGeneral(DM dmRow, PetscSection sectionRow, PetscSection globalSectionRow, DM dmCol, PetscSection sectionCol, PetscSection globalSectionCol, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7732 {
7733   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
7734   PetscInt          *indicesRow, *indicesCol;
7735   PetscInt           numIndicesRow, numIndicesCol;
7736   const PetscScalar *valuesOrig = values;
7737   PetscErrorCode     ierr;
7738 
7739   PetscFunctionBegin;
7740   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
7741   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
7742   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
7743   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
7744   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
7745   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 4);
7746   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
7747   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 5);
7748   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
7749   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 6);
7750   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7751 
7752   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7753   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7754 
7755   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7756   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7757   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
7758   if (ierr) {
7759     PetscMPIInt rank;
7760 
7761     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7762     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7763     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7764     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7765     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&values));
7766     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7767   }
7768 
7769   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7770   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7771   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7772   PetscFunctionReturn(0);
7773 }
7774 
7775 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7776 {
7777   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
7778   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7779   PetscInt       *cpoints = NULL;
7780   PetscInt       *findices, *cindices;
7781   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7782   PetscInt        foffsets[32], coffsets[32];
7783   DMPolytopeType  ct;
7784   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7785   PetscErrorCode  ierr;
7786 
7787   PetscFunctionBegin;
7788   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7789   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7790   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7791   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7792   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7793   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7794   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7795   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7796   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7797   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7798   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7799   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7800   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7801   PetscCall(PetscArrayzero(foffsets, 32));
7802   PetscCall(PetscArrayzero(coffsets, 32));
7803   /* Column indices */
7804   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7805   maxFPoints = numCPoints;
7806   /* Compress out points not in the section */
7807   /*   TODO: Squeeze out points with 0 dof as well */
7808   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7809   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
7810     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7811       cpoints[q * 2]     = cpoints[p];
7812       cpoints[q * 2 + 1] = cpoints[p + 1];
7813       ++q;
7814     }
7815   }
7816   numCPoints = q;
7817   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
7818     PetscInt fdof;
7819 
7820     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7821     if (!dof) continue;
7822     for (f = 0; f < numFields; ++f) {
7823       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7824       coffsets[f + 1] += fdof;
7825     }
7826     numCIndices += dof;
7827   }
7828   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
7829   /* Row indices */
7830   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7831   {
7832     DMPlexTransform tr;
7833     DMPolytopeType *rct;
7834     PetscInt       *rsize, *rcone, *rornt, Nt;
7835 
7836     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7837     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7838     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7839     numSubcells = rsize[Nt - 1];
7840     PetscCall(DMPlexTransformDestroy(&tr));
7841   }
7842   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
7843   for (r = 0, q = 0; r < numSubcells; ++r) {
7844     /* TODO Map from coarse to fine cells */
7845     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7846     /* Compress out points not in the section */
7847     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7848     for (p = 0; p < numFPoints * 2; p += 2) {
7849       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7850         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7851         if (!dof) continue;
7852         for (s = 0; s < q; ++s)
7853           if (fpoints[p] == ftotpoints[s * 2]) break;
7854         if (s < q) continue;
7855         ftotpoints[q * 2]     = fpoints[p];
7856         ftotpoints[q * 2 + 1] = fpoints[p + 1];
7857         ++q;
7858       }
7859     }
7860     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7861   }
7862   numFPoints = q;
7863   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
7864     PetscInt fdof;
7865 
7866     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
7867     if (!dof) continue;
7868     for (f = 0; f < numFields; ++f) {
7869       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
7870       foffsets[f + 1] += fdof;
7871     }
7872     numFIndices += dof;
7873   }
7874   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
7875 
7876   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
7877   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
7878   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7879   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7880   if (numFields) {
7881     const PetscInt **permsF[32] = {NULL};
7882     const PetscInt **permsC[32] = {NULL};
7883 
7884     for (f = 0; f < numFields; f++) {
7885       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
7886       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
7887     }
7888     for (p = 0; p < numFPoints; p++) {
7889       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
7890       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
7891     }
7892     for (p = 0; p < numCPoints; p++) {
7893       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
7894       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
7895     }
7896     for (f = 0; f < numFields; f++) {
7897       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
7898       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
7899     }
7900   } else {
7901     const PetscInt **permsF = NULL;
7902     const PetscInt **permsC = NULL;
7903 
7904     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
7905     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
7906     for (p = 0, off = 0; p < numFPoints; p++) {
7907       const PetscInt *perm = permsF ? permsF[p] : NULL;
7908 
7909       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
7910       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
7911     }
7912     for (p = 0, off = 0; p < numCPoints; p++) {
7913       const PetscInt *perm = permsC ? permsC[p] : NULL;
7914 
7915       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
7916       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
7917     }
7918     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
7919     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
7920   }
7921   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
7922   /* TODO: flips */
7923   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7924   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
7925   if (ierr) {
7926     PetscMPIInt rank;
7927 
7928     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7929     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7930     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
7931     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7932     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7933   }
7934   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
7935   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7936   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7937   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7938   PetscFunctionReturn(0);
7939 }
7940 
7941 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
7942 {
7943   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7944   PetscInt       *cpoints = NULL;
7945   PetscInt        foffsets[32], coffsets[32];
7946   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7947   DMPolytopeType  ct;
7948   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7949 
7950   PetscFunctionBegin;
7951   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7952   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7953   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7954   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7955   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7956   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7957   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7958   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7959   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7960   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7961   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7962   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7963   PetscCall(PetscArrayzero(foffsets, 32));
7964   PetscCall(PetscArrayzero(coffsets, 32));
7965   /* Column indices */
7966   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7967   maxFPoints = numCPoints;
7968   /* Compress out points not in the section */
7969   /*   TODO: Squeeze out points with 0 dof as well */
7970   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7971   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
7972     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7973       cpoints[q * 2]     = cpoints[p];
7974       cpoints[q * 2 + 1] = cpoints[p + 1];
7975       ++q;
7976     }
7977   }
7978   numCPoints = q;
7979   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
7980     PetscInt fdof;
7981 
7982     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7983     if (!dof) continue;
7984     for (f = 0; f < numFields; ++f) {
7985       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7986       coffsets[f + 1] += fdof;
7987     }
7988     numCIndices += dof;
7989   }
7990   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
7991   /* Row indices */
7992   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7993   {
7994     DMPlexTransform tr;
7995     DMPolytopeType *rct;
7996     PetscInt       *rsize, *rcone, *rornt, Nt;
7997 
7998     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7999     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8000     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8001     numSubcells = rsize[Nt - 1];
8002     PetscCall(DMPlexTransformDestroy(&tr));
8003   }
8004   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8005   for (r = 0, q = 0; r < numSubcells; ++r) {
8006     /* TODO Map from coarse to fine cells */
8007     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8008     /* Compress out points not in the section */
8009     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8010     for (p = 0; p < numFPoints * 2; p += 2) {
8011       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8012         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8013         if (!dof) continue;
8014         for (s = 0; s < q; ++s)
8015           if (fpoints[p] == ftotpoints[s * 2]) break;
8016         if (s < q) continue;
8017         ftotpoints[q * 2]     = fpoints[p];
8018         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8019         ++q;
8020       }
8021     }
8022     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8023   }
8024   numFPoints = q;
8025   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8026     PetscInt fdof;
8027 
8028     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8029     if (!dof) continue;
8030     for (f = 0; f < numFields; ++f) {
8031       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8032       foffsets[f + 1] += fdof;
8033     }
8034     numFIndices += dof;
8035   }
8036   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8037 
8038   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8039   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8040   if (numFields) {
8041     const PetscInt **permsF[32] = {NULL};
8042     const PetscInt **permsC[32] = {NULL};
8043 
8044     for (f = 0; f < numFields; f++) {
8045       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8046       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8047     }
8048     for (p = 0; p < numFPoints; p++) {
8049       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8050       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8051     }
8052     for (p = 0; p < numCPoints; p++) {
8053       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8054       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8055     }
8056     for (f = 0; f < numFields; f++) {
8057       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8058       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8059     }
8060   } else {
8061     const PetscInt **permsF = NULL;
8062     const PetscInt **permsC = NULL;
8063 
8064     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8065     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8066     for (p = 0, off = 0; p < numFPoints; p++) {
8067       const PetscInt *perm = permsF ? permsF[p] : NULL;
8068 
8069       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8070       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8071     }
8072     for (p = 0, off = 0; p < numCPoints; p++) {
8073       const PetscInt *perm = permsC ? permsC[p] : NULL;
8074 
8075       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8076       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8077     }
8078     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8079     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8080   }
8081   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8082   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8083   PetscFunctionReturn(0);
8084 }
8085 
8086 /*@C
8087   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8088 
8089   Input Parameter:
8090 . dm   - The DMPlex object
8091 
8092   Output Parameter:
8093 . cellHeight - The height of a cell
8094 
8095   Level: developer
8096 
8097 .seealso `DMPlexSetVTKCellHeight()`
8098 @*/
8099 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8100 {
8101   DM_Plex *mesh = (DM_Plex *)dm->data;
8102 
8103   PetscFunctionBegin;
8104   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8105   PetscValidIntPointer(cellHeight, 2);
8106   *cellHeight = mesh->vtkCellHeight;
8107   PetscFunctionReturn(0);
8108 }
8109 
8110 /*@C
8111   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8112 
8113   Input Parameters:
8114 + dm   - The DMPlex object
8115 - cellHeight - The height of a cell
8116 
8117   Level: developer
8118 
8119 .seealso `DMPlexGetVTKCellHeight()`
8120 @*/
8121 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8122 {
8123   DM_Plex *mesh = (DM_Plex *)dm->data;
8124 
8125   PetscFunctionBegin;
8126   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8127   mesh->vtkCellHeight = cellHeight;
8128   PetscFunctionReturn(0);
8129 }
8130 
8131 /*@
8132   DMPlexGetGhostCellStratum - Get the range of cells which are used to enforce FV boundary conditions
8133 
8134   Input Parameter:
8135 . dm - The DMPlex object
8136 
8137   Output Parameters:
8138 + gcStart - The first ghost cell, or NULL
8139 - gcEnd   - The upper bound on ghost cells, or NULL
8140 
8141   Level: advanced
8142 
8143 .seealso `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
8144 @*/
8145 PetscErrorCode DMPlexGetGhostCellStratum(DM dm, PetscInt *gcStart, PetscInt *gcEnd)
8146 {
8147   DMLabel ctLabel;
8148 
8149   PetscFunctionBegin;
8150   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8151   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
8152   PetscCall(DMLabelGetStratumBounds(ctLabel, DM_POLYTOPE_FV_GHOST, gcStart, gcEnd));
8153   // Reset label for fast lookup
8154   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
8155   PetscFunctionReturn(0);
8156 }
8157 
8158 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8159 {
8160   PetscSection section, globalSection;
8161   PetscInt    *numbers, p;
8162 
8163   PetscFunctionBegin;
8164   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8165   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8166   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8167   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8168   PetscCall(PetscSectionSetUp(section));
8169   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8170   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8171   for (p = pStart; p < pEnd; ++p) {
8172     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8173     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8174     else numbers[p - pStart] += shift;
8175   }
8176   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8177   if (globalSize) {
8178     PetscLayout layout;
8179     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8180     PetscCall(PetscLayoutGetSize(layout, globalSize));
8181     PetscCall(PetscLayoutDestroy(&layout));
8182   }
8183   PetscCall(PetscSectionDestroy(&section));
8184   PetscCall(PetscSectionDestroy(&globalSection));
8185   PetscFunctionReturn(0);
8186 }
8187 
8188 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8189 {
8190   PetscInt cellHeight, cStart, cEnd;
8191 
8192   PetscFunctionBegin;
8193   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8194   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8195   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8196   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8197   PetscFunctionReturn(0);
8198 }
8199 
8200 /*@
8201   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8202 
8203   Input Parameter:
8204 . dm   - The DMPlex object
8205 
8206   Output Parameter:
8207 . globalCellNumbers - Global cell numbers for all cells on this process
8208 
8209   Level: developer
8210 
8211 .seealso `DMPlexGetVertexNumbering()`
8212 @*/
8213 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8214 {
8215   DM_Plex *mesh = (DM_Plex *)dm->data;
8216 
8217   PetscFunctionBegin;
8218   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8219   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8220   *globalCellNumbers = mesh->globalCellNumbers;
8221   PetscFunctionReturn(0);
8222 }
8223 
8224 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8225 {
8226   PetscInt vStart, vEnd;
8227 
8228   PetscFunctionBegin;
8229   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8230   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8231   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8232   PetscFunctionReturn(0);
8233 }
8234 
8235 /*@
8236   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8237 
8238   Input Parameter:
8239 . dm   - The DMPlex object
8240 
8241   Output Parameter:
8242 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8243 
8244   Level: developer
8245 
8246 .seealso `DMPlexGetCellNumbering()`
8247 @*/
8248 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8249 {
8250   DM_Plex *mesh = (DM_Plex *)dm->data;
8251 
8252   PetscFunctionBegin;
8253   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8254   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8255   *globalVertexNumbers = mesh->globalVertexNumbers;
8256   PetscFunctionReturn(0);
8257 }
8258 
8259 /*@
8260   DMPlexCreatePointNumbering - Create a global numbering for all points.
8261 
8262   Collective on dm
8263 
8264   Input Parameter:
8265 . dm   - The DMPlex object
8266 
8267   Output Parameter:
8268 . globalPointNumbers - Global numbers for all points on this process
8269 
8270   Notes:
8271 
8272   The point numbering IS is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8273   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8274   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8275   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8276 
8277   The partitioned mesh is
8278 ```
8279  (2)--0--(3)--1--(4)    (1)--0--(2)
8280 ```
8281   and its global numbering is
8282 ```
8283   (3)--0--(4)--1--(5)--2--(6)
8284 ```
8285   Then the global numbering is provided as
8286 ```
8287 [0] Number of indices in set 5
8288 [0] 0 0
8289 [0] 1 1
8290 [0] 2 3
8291 [0] 3 4
8292 [0] 4 -6
8293 [1] Number of indices in set 3
8294 [1] 0 2
8295 [1] 1 5
8296 [1] 2 6
8297 ```
8298 
8299   Level: developer
8300 
8301 .seealso `DMPlexGetCellNumbering()`
8302 @*/
8303 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8304 {
8305   IS        nums[4];
8306   PetscInt  depths[4], gdepths[4], starts[4];
8307   PetscInt  depth, d, shift = 0;
8308   PetscBool empty = PETSC_FALSE;
8309 
8310   PetscFunctionBegin;
8311   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8312   PetscCall(DMPlexGetDepth(dm, &depth));
8313   // For unstratified meshes use dim instead of depth
8314   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8315   // If any stratum is empty, we must mark all empty
8316   for (d = 0; d <= depth; ++d) {
8317     PetscInt end;
8318 
8319     depths[d] = depth - d;
8320     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8321     if (!(starts[d] - end)) empty = PETSC_TRUE;
8322   }
8323   if (empty)
8324     for (d = 0; d <= depth; ++d) {
8325       depths[d] = -1;
8326       starts[d] = -1;
8327     }
8328   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8329   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8330   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]);
8331   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8332   for (d = 0; d <= depth; ++d) {
8333     PetscInt pStart, pEnd, gsize;
8334 
8335     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8336     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8337     shift += gsize;
8338   }
8339   PetscCall(ISConcatenate(PetscObjectComm((PetscObject)dm), depth + 1, nums, globalPointNumbers));
8340   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8341   PetscFunctionReturn(0);
8342 }
8343 
8344 /*@
8345   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8346 
8347   Input Parameter:
8348 . dm - The DMPlex object
8349 
8350   Output Parameter:
8351 . ranks - The rank field
8352 
8353   Options Database Keys:
8354 . -dm_partition_view - Adds the rank field into the DM output from -dm_view using the same viewer
8355 
8356   Level: intermediate
8357 
8358 .seealso: `DMView()`
8359 @*/
8360 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8361 {
8362   DM             rdm;
8363   PetscFE        fe;
8364   PetscScalar   *r;
8365   PetscMPIInt    rank;
8366   DMPolytopeType ct;
8367   PetscInt       dim, cStart, cEnd, c;
8368   PetscBool      simplex;
8369 
8370   PetscFunctionBeginUser;
8371   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8372   PetscValidPointer(ranks, 2);
8373   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8374   PetscCall(DMClone(dm, &rdm));
8375   PetscCall(DMGetDimension(rdm, &dim));
8376   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8377   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8378   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8379   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8380   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8381   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8382   PetscCall(PetscFEDestroy(&fe));
8383   PetscCall(DMCreateDS(rdm));
8384   PetscCall(DMCreateGlobalVector(rdm, ranks));
8385   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8386   PetscCall(VecGetArray(*ranks, &r));
8387   for (c = cStart; c < cEnd; ++c) {
8388     PetscScalar *lr;
8389 
8390     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8391     if (lr) *lr = rank;
8392   }
8393   PetscCall(VecRestoreArray(*ranks, &r));
8394   PetscCall(DMDestroy(&rdm));
8395   PetscFunctionReturn(0);
8396 }
8397 
8398 /*@
8399   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8400 
8401   Input Parameters:
8402 + dm    - The DMPlex
8403 - label - The DMLabel
8404 
8405   Output Parameter:
8406 . val - The label value field
8407 
8408   Options Database Keys:
8409 . -dm_label_view - Adds the label value field into the DM output from -dm_view using the same viewer
8410 
8411   Level: intermediate
8412 
8413 .seealso: `DMView()`
8414 @*/
8415 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8416 {
8417   DM           rdm;
8418   PetscFE      fe;
8419   PetscScalar *v;
8420   PetscInt     dim, cStart, cEnd, c;
8421 
8422   PetscFunctionBeginUser;
8423   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8424   PetscValidPointer(label, 2);
8425   PetscValidPointer(val, 3);
8426   PetscCall(DMClone(dm, &rdm));
8427   PetscCall(DMGetDimension(rdm, &dim));
8428   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject)rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8429   PetscCall(PetscObjectSetName((PetscObject)fe, "label_value"));
8430   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8431   PetscCall(PetscFEDestroy(&fe));
8432   PetscCall(DMCreateDS(rdm));
8433   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8434   PetscCall(DMCreateGlobalVector(rdm, val));
8435   PetscCall(PetscObjectSetName((PetscObject)*val, "label_value"));
8436   PetscCall(VecGetArray(*val, &v));
8437   for (c = cStart; c < cEnd; ++c) {
8438     PetscScalar *lv;
8439     PetscInt     cval;
8440 
8441     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8442     PetscCall(DMLabelGetValue(label, c, &cval));
8443     *lv = cval;
8444   }
8445   PetscCall(VecRestoreArray(*val, &v));
8446   PetscCall(DMDestroy(&rdm));
8447   PetscFunctionReturn(0);
8448 }
8449 
8450 /*@
8451   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8452 
8453   Input Parameter:
8454 . dm - The DMPlex object
8455 
8456   Notes:
8457   This is a useful diagnostic when creating meshes programmatically.
8458 
8459   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8460 
8461   Level: developer
8462 
8463 .seealso: `DMCreate()`, `DMSetFromOptions()`
8464 @*/
8465 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8466 {
8467   PetscSection    coneSection, supportSection;
8468   const PetscInt *cone, *support;
8469   PetscInt        coneSize, c, supportSize, s;
8470   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8471   PetscBool       storagecheck = PETSC_TRUE;
8472 
8473   PetscFunctionBegin;
8474   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8475   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8476   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8477   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8478   /* Check that point p is found in the support of its cone points, and vice versa */
8479   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8480   for (p = pStart; p < pEnd; ++p) {
8481     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8482     PetscCall(DMPlexGetCone(dm, p, &cone));
8483     for (c = 0; c < coneSize; ++c) {
8484       PetscBool dup = PETSC_FALSE;
8485       PetscInt  d;
8486       for (d = c - 1; d >= 0; --d) {
8487         if (cone[c] == cone[d]) {
8488           dup = PETSC_TRUE;
8489           break;
8490         }
8491       }
8492       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8493       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8494       for (s = 0; s < supportSize; ++s) {
8495         if (support[s] == p) break;
8496       }
8497       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
8498         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8499         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8500         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8501         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8502         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8503         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8504         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]);
8505         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8506       }
8507     }
8508     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8509     if (p != pp) {
8510       storagecheck = PETSC_FALSE;
8511       continue;
8512     }
8513     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8514     PetscCall(DMPlexGetSupport(dm, p, &support));
8515     for (s = 0; s < supportSize; ++s) {
8516       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8517       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8518       for (c = 0; c < coneSize; ++c) {
8519         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8520         if (cone[c] != pp) {
8521           c = 0;
8522           break;
8523         }
8524         if (cone[c] == p) break;
8525       }
8526       if (c >= coneSize) {
8527         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8528         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8529         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8530         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8531         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8532         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8533         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8534       }
8535     }
8536   }
8537   if (storagecheck) {
8538     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8539     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8540     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8541   }
8542   PetscFunctionReturn(0);
8543 }
8544 
8545 /*
8546   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.
8547 */
8548 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
8549 {
8550   DMPolytopeType  cct;
8551   PetscInt        ptpoints[4];
8552   const PetscInt *cone, *ccone, *ptcone;
8553   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8554 
8555   PetscFunctionBegin;
8556   *unsplit = 0;
8557   switch (ct) {
8558   case DM_POLYTOPE_POINT_PRISM_TENSOR:
8559     ptpoints[npt++] = c;
8560     break;
8561   case DM_POLYTOPE_SEG_PRISM_TENSOR:
8562     PetscCall(DMPlexGetCone(dm, c, &cone));
8563     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8564     for (cp = 0; cp < coneSize; ++cp) {
8565       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8566       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8567     }
8568     break;
8569   case DM_POLYTOPE_TRI_PRISM_TENSOR:
8570   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8571     PetscCall(DMPlexGetCone(dm, c, &cone));
8572     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8573     for (cp = 0; cp < coneSize; ++cp) {
8574       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8575       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8576       for (ccp = 0; ccp < cconeSize; ++ccp) {
8577         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8578         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8579           PetscInt p;
8580           for (p = 0; p < npt; ++p)
8581             if (ptpoints[p] == ccone[ccp]) break;
8582           if (p == npt) ptpoints[npt++] = ccone[ccp];
8583         }
8584       }
8585     }
8586     break;
8587   default:
8588     break;
8589   }
8590   for (pt = 0; pt < npt; ++pt) {
8591     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8592     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8593   }
8594   PetscFunctionReturn(0);
8595 }
8596 
8597 /*@
8598   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8599 
8600   Input Parameters:
8601 + dm - The DMPlex object
8602 - cellHeight - Normally 0
8603 
8604   Notes:
8605   This is a useful diagnostic when creating meshes programmatically.
8606   Currently applicable only to homogeneous simplex or tensor meshes.
8607 
8608   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8609 
8610   Level: developer
8611 
8612 .seealso: `DMCreate()`, `DMSetFromOptions()`
8613 @*/
8614 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
8615 {
8616   DMPlexInterpolatedFlag interp;
8617   DMPolytopeType         ct;
8618   PetscInt               vStart, vEnd, cStart, cEnd, c;
8619 
8620   PetscFunctionBegin;
8621   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8622   PetscCall(DMPlexIsInterpolated(dm, &interp));
8623   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8624   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8625   for (c = cStart; c < cEnd; ++c) {
8626     PetscInt *closure = NULL;
8627     PetscInt  coneSize, closureSize, cl, Nv = 0;
8628 
8629     PetscCall(DMPlexGetCellType(dm, c, &ct));
8630     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
8631     if (ct == DM_POLYTOPE_UNKNOWN) continue;
8632     if (interp == DMPLEX_INTERPOLATED_FULL) {
8633       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8634       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));
8635     }
8636     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8637     for (cl = 0; cl < closureSize * 2; cl += 2) {
8638       const PetscInt p = closure[cl];
8639       if ((p >= vStart) && (p < vEnd)) ++Nv;
8640     }
8641     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8642     /* Special Case: Tensor faces with identified vertices */
8643     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
8644       PetscInt unsplit;
8645 
8646       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8647       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
8648     }
8649     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));
8650   }
8651   PetscFunctionReturn(0);
8652 }
8653 
8654 /*@
8655   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
8656 
8657   Collective
8658 
8659   Input Parameters:
8660 + dm - The DMPlex object
8661 - cellHeight - Normally 0
8662 
8663   Notes:
8664   This is a useful diagnostic when creating meshes programmatically.
8665   This routine is only relevant for meshes that are fully interpolated across all ranks.
8666   It will error out if a partially interpolated mesh is given on some rank.
8667   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
8668 
8669   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8670 
8671   Level: developer
8672 
8673 .seealso: `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
8674 @*/
8675 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
8676 {
8677   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
8678   DMPlexInterpolatedFlag interpEnum;
8679 
8680   PetscFunctionBegin;
8681   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8682   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
8683   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(0);
8684   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
8685     PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported");
8686     PetscFunctionReturn(0);
8687   }
8688 
8689   PetscCall(DMGetDimension(dm, &dim));
8690   PetscCall(DMPlexGetDepth(dm, &depth));
8691   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8692   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
8693     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
8694     for (c = cStart; c < cEnd; ++c) {
8695       const PetscInt       *cone, *ornt, *faceSizes, *faces;
8696       const DMPolytopeType *faceTypes;
8697       DMPolytopeType        ct;
8698       PetscInt              numFaces, coneSize, f;
8699       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
8700 
8701       PetscCall(DMPlexGetCellType(dm, c, &ct));
8702       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8703       if (unsplit) continue;
8704       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8705       PetscCall(DMPlexGetCone(dm, c, &cone));
8706       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
8707       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8708       for (cl = 0; cl < closureSize * 2; cl += 2) {
8709         const PetscInt p = closure[cl];
8710         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
8711       }
8712       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8713       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);
8714       for (f = 0; f < numFaces; ++f) {
8715         DMPolytopeType fct;
8716         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
8717 
8718         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
8719         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
8720         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
8721           const PetscInt p = fclosure[cl];
8722           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
8723         }
8724         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]);
8725         for (v = 0; v < fnumCorners; ++v) {
8726           if (fclosure[v] != faces[fOff + v]) {
8727             PetscInt v1;
8728 
8729             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
8730             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
8731             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
8732             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
8733             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8734             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]);
8735           }
8736         }
8737         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
8738         fOff += faceSizes[f];
8739       }
8740       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8741       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8742     }
8743   }
8744   PetscFunctionReturn(0);
8745 }
8746 
8747 /*@
8748   DMPlexCheckGeometry - Check the geometry of mesh cells
8749 
8750   Input Parameter:
8751 . dm - The DMPlex object
8752 
8753   Notes:
8754   This is a useful diagnostic when creating meshes programmatically.
8755 
8756   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8757 
8758   Level: developer
8759 
8760 .seealso: `DMCreate()`, `DMSetFromOptions()`
8761 @*/
8762 PetscErrorCode DMPlexCheckGeometry(DM dm)
8763 {
8764   Vec       coordinates;
8765   PetscReal detJ, J[9], refVol = 1.0;
8766   PetscReal vol;
8767   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
8768 
8769   PetscFunctionBegin;
8770   PetscCall(DMGetDimension(dm, &dim));
8771   PetscCall(DMGetCoordinateDim(dm, &dE));
8772   if (dim != dE) PetscFunctionReturn(0);
8773   PetscCall(DMPlexGetDepth(dm, &depth));
8774   for (d = 0; d < dim; ++d) refVol *= 2.0;
8775   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
8776   /* Make sure local coordinates are created, because that step is collective */
8777   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
8778   for (c = cStart; c < cEnd; ++c) {
8779     DMPolytopeType ct;
8780     PetscInt       unsplit;
8781     PetscBool      ignoreZeroVol = PETSC_FALSE;
8782 
8783     PetscCall(DMPlexGetCellType(dm, c, &ct));
8784     switch (ct) {
8785     case DM_POLYTOPE_SEG_PRISM_TENSOR:
8786     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8787     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8788       ignoreZeroVol = PETSC_TRUE;
8789       break;
8790     default:
8791       break;
8792     }
8793     switch (ct) {
8794     case DM_POLYTOPE_TRI_PRISM:
8795     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8796     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8797     case DM_POLYTOPE_PYRAMID:
8798       continue;
8799     default:
8800       break;
8801     }
8802     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8803     if (unsplit) continue;
8804     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
8805     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);
8806     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
8807     /* This should work with periodicity since DG coordinates should be used */
8808     if (depth > 1) {
8809       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
8810       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);
8811       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
8812     }
8813   }
8814   PetscFunctionReturn(0);
8815 }
8816 
8817 /*@
8818   DMPlexCheckPointSF - Check that several necessary conditions are met for the Point SF of this plex.
8819 
8820   Collective
8821 
8822   Input Parameters:
8823 + dm - The DMPlex object
8824 . pointSF - The Point SF, or NULL for Point SF attached to DM
8825 - allowExtraRoots - Flag to allow extra points not present in the DM
8826 
8827   Notes:
8828   This is mainly intended for debugging/testing purposes.
8829 
8830   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8831 
8832   Extra roots can come from priodic cuts, where additional points appear on the boundary
8833 
8834   Level: developer
8835 
8836 .seealso: `DMGetPointSF()`, `DMSetFromOptions()`
8837 @*/
8838 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
8839 {
8840   PetscInt           l, nleaves, nroots, overlap;
8841   const PetscInt    *locals;
8842   const PetscSFNode *remotes;
8843   PetscBool          distributed;
8844   MPI_Comm           comm;
8845   PetscMPIInt        rank;
8846 
8847   PetscFunctionBegin;
8848   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8849   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
8850   else pointSF = dm->sf;
8851   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
8852   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
8853   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8854   {
8855     PetscMPIInt mpiFlag;
8856 
8857     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
8858     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
8859   }
8860   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
8861   PetscCall(DMPlexIsDistributed(dm, &distributed));
8862   if (!distributed) {
8863     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);
8864     PetscFunctionReturn(0);
8865   }
8866   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);
8867   PetscCall(DMPlexGetOverlap(dm, &overlap));
8868 
8869   /* Check SF graph is compatible with DMPlex chart */
8870   {
8871     PetscInt pStart, pEnd, maxLeaf;
8872 
8873     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8874     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
8875     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
8876     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
8877   }
8878 
8879   /* Check Point SF has no local points referenced */
8880   for (l = 0; l < nleaves; l++) {
8881     PetscAssert(remotes[l].rank != (PetscInt)rank, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains local point %" PetscInt_FMT " <- (%" PetscInt_FMT ",%" PetscInt_FMT ")", locals ? locals[l] : l, remotes[l].rank, remotes[l].index);
8882   }
8883 
8884   /* Check there are no cells in interface */
8885   if (!overlap) {
8886     PetscInt cellHeight, cStart, cEnd;
8887 
8888     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8889     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8890     for (l = 0; l < nleaves; ++l) {
8891       const PetscInt point = locals ? locals[l] : l;
8892 
8893       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
8894     }
8895   }
8896 
8897   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
8898   {
8899     const PetscInt *rootdegree;
8900 
8901     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
8902     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
8903     for (l = 0; l < nleaves; ++l) {
8904       const PetscInt  point = locals ? locals[l] : l;
8905       const PetscInt *cone;
8906       PetscInt        coneSize, c, idx;
8907 
8908       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
8909       PetscCall(DMPlexGetCone(dm, point, &cone));
8910       for (c = 0; c < coneSize; ++c) {
8911         if (!rootdegree[cone[c]]) {
8912           if (locals) {
8913             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
8914           } else {
8915             idx = (cone[c] < nleaves) ? cone[c] : -1;
8916           }
8917           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
8918         }
8919       }
8920     }
8921   }
8922   PetscFunctionReturn(0);
8923 }
8924 
8925 /*@
8926   DMPlexCheck - Perform various checks of Plex sanity
8927 
8928   Input Parameter:
8929 . dm - The DMPlex object
8930 
8931   Notes:
8932   This is a useful diagnostic when creating meshes programmatically.
8933 
8934   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8935 
8936   Currently does not include DMPlexCheckCellShape().
8937 
8938   Level: developer
8939 
8940 .seealso: DMCreate(), DMSetFromOptions()
8941 @*/
8942 PetscErrorCode DMPlexCheck(DM dm)
8943 {
8944   PetscInt cellHeight;
8945 
8946   PetscFunctionBegin;
8947   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8948   PetscCall(DMPlexCheckSymmetry(dm));
8949   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
8950   PetscCall(DMPlexCheckFaces(dm, cellHeight));
8951   PetscCall(DMPlexCheckGeometry(dm));
8952   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
8953   PetscCall(DMPlexCheckInterfaceCones(dm));
8954   PetscFunctionReturn(0);
8955 }
8956 
8957 typedef struct cell_stats {
8958   PetscReal min, max, sum, squaresum;
8959   PetscInt  count;
8960 } cell_stats_t;
8961 
8962 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
8963 {
8964   PetscInt i, N = *len;
8965 
8966   for (i = 0; i < N; i++) {
8967     cell_stats_t *A = (cell_stats_t *)a;
8968     cell_stats_t *B = (cell_stats_t *)b;
8969 
8970     B->min = PetscMin(A->min, B->min);
8971     B->max = PetscMax(A->max, B->max);
8972     B->sum += A->sum;
8973     B->squaresum += A->squaresum;
8974     B->count += A->count;
8975   }
8976 }
8977 
8978 /*@
8979   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
8980 
8981   Collective on dm
8982 
8983   Input Parameters:
8984 + dm        - The DMPlex object
8985 . output    - If true, statistics will be displayed on stdout
8986 - condLimit - Display all cells above this condition number, or PETSC_DETERMINE for no cell output
8987 
8988   Notes:
8989   This is mainly intended for debugging/testing purposes.
8990 
8991   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8992 
8993   Level: developer
8994 
8995 .seealso: `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
8996 @*/
8997 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
8998 {
8999   DM           dmCoarse;
9000   cell_stats_t stats, globalStats;
9001   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9002   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9003   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9004   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9005   PetscMPIInt  rank, size;
9006 
9007   PetscFunctionBegin;
9008   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9009   stats.min = PETSC_MAX_REAL;
9010   stats.max = PETSC_MIN_REAL;
9011   stats.sum = stats.squaresum = 0.;
9012   stats.count                 = 0;
9013 
9014   PetscCallMPI(MPI_Comm_size(comm, &size));
9015   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9016   PetscCall(DMGetCoordinateDim(dm, &cdim));
9017   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9018   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9019   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9020   for (c = cStart; c < cEnd; c++) {
9021     PetscInt  i;
9022     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9023 
9024     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9025     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9026     for (i = 0; i < PetscSqr(cdim); ++i) {
9027       frobJ += J[i] * J[i];
9028       frobInvJ += invJ[i] * invJ[i];
9029     }
9030     cond2 = frobJ * frobInvJ;
9031     cond  = PetscSqrtReal(cond2);
9032 
9033     stats.min = PetscMin(stats.min, cond);
9034     stats.max = PetscMax(stats.max, cond);
9035     stats.sum += cond;
9036     stats.squaresum += cond2;
9037     stats.count++;
9038     if (output && cond > limit) {
9039       PetscSection coordSection;
9040       Vec          coordsLocal;
9041       PetscScalar *coords = NULL;
9042       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9043 
9044       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9045       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9046       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9047       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9048       for (i = 0; i < Nv / cdim; ++i) {
9049         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9050         for (d = 0; d < cdim; ++d) {
9051           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9052           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9053         }
9054         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9055       }
9056       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9057       for (cl = 0; cl < clSize * 2; cl += 2) {
9058         const PetscInt edge = closure[cl];
9059 
9060         if ((edge >= eStart) && (edge < eEnd)) {
9061           PetscReal len;
9062 
9063           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9064           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9065         }
9066       }
9067       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9068       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9069     }
9070   }
9071   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9072 
9073   if (size > 1) {
9074     PetscMPIInt  blockLengths[2] = {4, 1};
9075     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9076     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9077     MPI_Op       statReduce;
9078 
9079     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9080     PetscCallMPI(MPI_Type_commit(&statType));
9081     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9082     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9083     PetscCallMPI(MPI_Op_free(&statReduce));
9084     PetscCallMPI(MPI_Type_free(&statType));
9085   } else {
9086     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9087   }
9088   if (rank == 0) {
9089     count = globalStats.count;
9090     min   = globalStats.min;
9091     max   = globalStats.max;
9092     mean  = globalStats.sum / globalStats.count;
9093     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9094   }
9095 
9096   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));
9097   PetscCall(PetscFree2(J, invJ));
9098 
9099   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9100   if (dmCoarse) {
9101     PetscBool isplex;
9102 
9103     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9104     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9105   }
9106   PetscFunctionReturn(0);
9107 }
9108 
9109 /*@
9110   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9111   orthogonal quality below given tolerance.
9112 
9113   Collective on dm
9114 
9115   Input Parameters:
9116 + dm   - The DMPlex object
9117 . fv   - Optional PetscFV object for pre-computed cell/face centroid information
9118 - atol - [0, 1] Absolute tolerance for tagging cells.
9119 
9120   Output Parameters:
9121 + OrthQual      - Vec containing orthogonal quality per cell
9122 - OrthQualLabel - DMLabel tagging cells below atol with DM_ADAPT_REFINE
9123 
9124   Options Database Keys:
9125 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only PETSCVIEWERASCII is
9126 supported.
9127 - -dm_plex_orthogonal_quality_vec_view - view OrthQual vector.
9128 
9129   Notes:
9130   Orthogonal quality is given by the following formula:
9131 
9132   \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]
9133 
9134   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
9135   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9136   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9137   calculating the cosine of the angle between these vectors.
9138 
9139   Orthogonal quality ranges from 1 (best) to 0 (worst).
9140 
9141   This routine is mainly useful for FVM, however is not restricted to only FVM. The PetscFV object is optionally used to check for
9142   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9143 
9144   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9145 
9146   Level: intermediate
9147 
9148 .seealso: `DMPlexCheckCellShape()`, `DMCreateLabel()`
9149 @*/
9150 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9151 {
9152   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9153   PetscInt              *idx;
9154   PetscScalar           *oqVals;
9155   const PetscScalar     *cellGeomArr, *faceGeomArr;
9156   PetscReal             *ci, *fi, *Ai;
9157   MPI_Comm               comm;
9158   Vec                    cellgeom, facegeom;
9159   DM                     dmFace, dmCell;
9160   IS                     glob;
9161   ISLocalToGlobalMapping ltog;
9162   PetscViewer            vwr;
9163 
9164   PetscFunctionBegin;
9165   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9166   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9167   PetscValidPointer(OrthQual, 4);
9168   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9169   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9170   PetscCall(DMGetDimension(dm, &nc));
9171   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9172   {
9173     DMPlexInterpolatedFlag interpFlag;
9174 
9175     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9176     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9177       PetscMPIInt rank;
9178 
9179       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9180       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9181     }
9182   }
9183   if (OrthQualLabel) {
9184     PetscValidPointer(OrthQualLabel, 5);
9185     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9186     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9187   } else {
9188     *OrthQualLabel = NULL;
9189   }
9190   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9191   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9192   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9193   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9194   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9195   PetscCall(VecCreate(comm, OrthQual));
9196   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9197   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9198   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9199   PetscCall(VecSetUp(*OrthQual));
9200   PetscCall(ISDestroy(&glob));
9201   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9202   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9203   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9204   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9205   PetscCall(VecGetDM(cellgeom, &dmCell));
9206   PetscCall(VecGetDM(facegeom, &dmFace));
9207   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9208   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9209     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9210     PetscInt         cellarr[2], *adj = NULL;
9211     PetscScalar     *cArr, *fArr;
9212     PetscReal        minvalc = 1.0, minvalf = 1.0;
9213     PetscFVCellGeom *cg;
9214 
9215     idx[cellIter] = cell - cStart;
9216     cellarr[0]    = cell;
9217     /* Make indexing into cellGeom easier */
9218     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9219     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9220     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9221     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9222     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9223       PetscInt         i;
9224       const PetscInt   neigh  = adj[cellneigh];
9225       PetscReal        normci = 0, normfi = 0, normai = 0;
9226       PetscFVCellGeom *cgneigh;
9227       PetscFVFaceGeom *fg;
9228 
9229       /* Don't count ourselves in the neighbor list */
9230       if (neigh == cell) continue;
9231       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9232       cellarr[1] = neigh;
9233       {
9234         PetscInt        numcovpts;
9235         const PetscInt *covpts;
9236 
9237         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9238         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9239         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9240       }
9241 
9242       /* Compute c_i, f_i and their norms */
9243       for (i = 0; i < nc; i++) {
9244         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9245         fi[i] = fg->centroid[i] - cg->centroid[i];
9246         Ai[i] = fg->normal[i];
9247         normci += PetscPowReal(ci[i], 2);
9248         normfi += PetscPowReal(fi[i], 2);
9249         normai += PetscPowReal(Ai[i], 2);
9250       }
9251       normci = PetscSqrtReal(normci);
9252       normfi = PetscSqrtReal(normfi);
9253       normai = PetscSqrtReal(normai);
9254 
9255       /* Normalize and compute for each face-cell-normal pair */
9256       for (i = 0; i < nc; i++) {
9257         ci[i] = ci[i] / normci;
9258         fi[i] = fi[i] / normfi;
9259         Ai[i] = Ai[i] / normai;
9260         /* PetscAbs because I don't know if normals are guaranteed to point out */
9261         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9262         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9263       }
9264       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9265       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9266     }
9267     PetscCall(PetscFree(adj));
9268     PetscCall(PetscFree2(cArr, fArr));
9269     /* Defer to cell if they're equal */
9270     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9271     if (OrthQualLabel) {
9272       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9273     }
9274   }
9275   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9276   PetscCall(VecAssemblyBegin(*OrthQual));
9277   PetscCall(VecAssemblyEnd(*OrthQual));
9278   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9279   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9280   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9281   if (OrthQualLabel) {
9282     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9283   }
9284   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9285   PetscCall(PetscViewerDestroy(&vwr));
9286   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9287   PetscFunctionReturn(0);
9288 }
9289 
9290 /* this is here insead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9291  * interpolator construction */
9292 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9293 {
9294   PetscSection section, newSection, gsection;
9295   PetscSF      sf;
9296   PetscBool    hasConstraints, ghasConstraints;
9297 
9298   PetscFunctionBegin;
9299   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9300   PetscValidPointer(odm, 2);
9301   PetscCall(DMGetLocalSection(dm, &section));
9302   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9303   PetscCallMPI(MPI_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9304   if (!ghasConstraints) {
9305     PetscCall(PetscObjectReference((PetscObject)dm));
9306     *odm = dm;
9307     PetscFunctionReturn(0);
9308   }
9309   PetscCall(DMClone(dm, odm));
9310   PetscCall(DMCopyFields(dm, *odm));
9311   PetscCall(DMGetLocalSection(*odm, &newSection));
9312   PetscCall(DMGetPointSF(*odm, &sf));
9313   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9314   PetscCall(DMSetGlobalSection(*odm, gsection));
9315   PetscCall(PetscSectionDestroy(&gsection));
9316   PetscFunctionReturn(0);
9317 }
9318 
9319 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9320 {
9321   DM        dmco, dmfo;
9322   Mat       interpo;
9323   Vec       rscale;
9324   Vec       cglobalo, clocal;
9325   Vec       fglobal, fglobalo, flocal;
9326   PetscBool regular;
9327 
9328   PetscFunctionBegin;
9329   PetscCall(DMGetFullDM(dmc, &dmco));
9330   PetscCall(DMGetFullDM(dmf, &dmfo));
9331   PetscCall(DMSetCoarseDM(dmfo, dmco));
9332   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9333   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9334   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9335   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9336   PetscCall(DMCreateLocalVector(dmc, &clocal));
9337   PetscCall(VecSet(cglobalo, 0.));
9338   PetscCall(VecSet(clocal, 0.));
9339   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9340   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9341   PetscCall(DMCreateLocalVector(dmf, &flocal));
9342   PetscCall(VecSet(fglobal, 0.));
9343   PetscCall(VecSet(fglobalo, 0.));
9344   PetscCall(VecSet(flocal, 0.));
9345   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9346   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9347   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9348   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9349   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9350   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9351   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9352   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9353   *shift = fglobal;
9354   PetscCall(VecDestroy(&flocal));
9355   PetscCall(VecDestroy(&fglobalo));
9356   PetscCall(VecDestroy(&clocal));
9357   PetscCall(VecDestroy(&cglobalo));
9358   PetscCall(VecDestroy(&rscale));
9359   PetscCall(MatDestroy(&interpo));
9360   PetscCall(DMDestroy(&dmfo));
9361   PetscCall(DMDestroy(&dmco));
9362   PetscFunctionReturn(0);
9363 }
9364 
9365 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9366 {
9367   PetscObject shifto;
9368   Vec         shift;
9369 
9370   PetscFunctionBegin;
9371   if (!interp) {
9372     Vec rscale;
9373 
9374     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9375     PetscCall(VecDestroy(&rscale));
9376   } else {
9377     PetscCall(PetscObjectReference((PetscObject)interp));
9378   }
9379   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9380   if (!shifto) {
9381     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9382     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9383     shifto = (PetscObject)shift;
9384     PetscCall(VecDestroy(&shift));
9385   }
9386   shift = (Vec)shifto;
9387   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9388   PetscCall(VecAXPY(fineSol, 1.0, shift));
9389   PetscCall(MatDestroy(&interp));
9390   PetscFunctionReturn(0);
9391 }
9392 
9393 /* Pointwise interpolation
9394      Just code FEM for now
9395      u^f = I u^c
9396      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9397      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9398      I_{ij} = psi^f_i phi^c_j
9399 */
9400 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9401 {
9402   PetscSection gsc, gsf;
9403   PetscInt     m, n;
9404   void        *ctx;
9405   DM           cdm;
9406   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9407 
9408   PetscFunctionBegin;
9409   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9410   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9411   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9412   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9413 
9414   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9415   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
9416   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9417   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9418   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9419 
9420   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9421   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9422   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9423   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9424   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9425   if (scaling) {
9426     /* Use naive scaling */
9427     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9428   }
9429   PetscFunctionReturn(0);
9430 }
9431 
9432 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9433 {
9434   VecScatter ctx;
9435 
9436   PetscFunctionBegin;
9437   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9438   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9439   PetscCall(VecScatterDestroy(&ctx));
9440   PetscFunctionReturn(0);
9441 }
9442 
9443 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[])
9444 {
9445   const PetscInt Nc = uOff[1] - uOff[0];
9446   PetscInt       c;
9447   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
9448 }
9449 
9450 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9451 {
9452   DM           dmc;
9453   PetscDS      ds;
9454   Vec          ones, locmass;
9455   IS           cellIS;
9456   PetscFormKey key;
9457   PetscInt     depth;
9458 
9459   PetscFunctionBegin;
9460   PetscCall(DMClone(dm, &dmc));
9461   PetscCall(DMCopyDisc(dm, dmc));
9462   PetscCall(DMGetDS(dmc, &ds));
9463   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9464   PetscCall(DMCreateGlobalVector(dmc, mass));
9465   PetscCall(DMGetLocalVector(dmc, &ones));
9466   PetscCall(DMGetLocalVector(dmc, &locmass));
9467   PetscCall(DMPlexGetDepth(dmc, &depth));
9468   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9469   PetscCall(VecSet(locmass, 0.0));
9470   PetscCall(VecSet(ones, 1.0));
9471   key.label = NULL;
9472   key.value = 0;
9473   key.field = 0;
9474   key.part  = 0;
9475   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9476   PetscCall(ISDestroy(&cellIS));
9477   PetscCall(VecSet(*mass, 0.0));
9478   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9479   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9480   PetscCall(DMRestoreLocalVector(dmc, &ones));
9481   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9482   PetscCall(DMDestroy(&dmc));
9483   PetscFunctionReturn(0);
9484 }
9485 
9486 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9487 {
9488   PetscSection gsc, gsf;
9489   PetscInt     m, n;
9490   void        *ctx;
9491   DM           cdm;
9492   PetscBool    regular;
9493 
9494   PetscFunctionBegin;
9495   if (dmFine == dmCoarse) {
9496     DM            dmc;
9497     PetscDS       ds;
9498     PetscWeakForm wf;
9499     Vec           u;
9500     IS            cellIS;
9501     PetscFormKey  key;
9502     PetscInt      depth;
9503 
9504     PetscCall(DMClone(dmFine, &dmc));
9505     PetscCall(DMCopyDisc(dmFine, dmc));
9506     PetscCall(DMGetDS(dmc, &ds));
9507     PetscCall(PetscDSGetWeakForm(ds, &wf));
9508     PetscCall(PetscWeakFormClear(wf));
9509     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9510     PetscCall(DMCreateMatrix(dmc, mass));
9511     PetscCall(DMGetLocalVector(dmc, &u));
9512     PetscCall(DMPlexGetDepth(dmc, &depth));
9513     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9514     PetscCall(MatZeroEntries(*mass));
9515     key.label = NULL;
9516     key.value = 0;
9517     key.field = 0;
9518     key.part  = 0;
9519     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9520     PetscCall(ISDestroy(&cellIS));
9521     PetscCall(DMRestoreLocalVector(dmc, &u));
9522     PetscCall(DMDestroy(&dmc));
9523   } else {
9524     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9525     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9526     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9527     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9528 
9529     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
9530     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9531     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9532     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9533 
9534     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9535     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9536     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9537     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9538   }
9539   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9540   PetscFunctionReturn(0);
9541 }
9542 
9543 /*@
9544   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9545 
9546   Input Parameter:
9547 . dm - The DMPlex object
9548 
9549   Output Parameter:
9550 . regular - The flag
9551 
9552   Level: intermediate
9553 
9554 .seealso: `DMPlexSetRegularRefinement()`
9555 @*/
9556 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
9557 {
9558   PetscFunctionBegin;
9559   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9560   PetscValidBoolPointer(regular, 2);
9561   *regular = ((DM_Plex *)dm->data)->regularRefinement;
9562   PetscFunctionReturn(0);
9563 }
9564 
9565 /*@
9566   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9567 
9568   Input Parameters:
9569 + dm - The DMPlex object
9570 - regular - The flag
9571 
9572   Level: intermediate
9573 
9574 .seealso: `DMPlexGetRegularRefinement()`
9575 @*/
9576 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
9577 {
9578   PetscFunctionBegin;
9579   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9580   ((DM_Plex *)dm->data)->regularRefinement = regular;
9581   PetscFunctionReturn(0);
9582 }
9583 
9584 /* anchors */
9585 /*@
9586   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9587   call DMPlexGetAnchors() directly: if there are anchors, then DMPlexGetAnchors() is called during DMGetDefaultConstraints().
9588 
9589   not collective
9590 
9591   Input Parameter:
9592 . dm - The DMPlex object
9593 
9594   Output Parameters:
9595 + anchorSection - If not NULL, set to the section describing which points anchor the constrained points.
9596 - anchorIS - If not NULL, set to the list of anchors indexed by anchorSection
9597 
9598   Level: intermediate
9599 
9600 .seealso: `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9601 @*/
9602 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
9603 {
9604   DM_Plex *plex = (DM_Plex *)dm->data;
9605 
9606   PetscFunctionBegin;
9607   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9608   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9609   if (anchorSection) *anchorSection = plex->anchorSection;
9610   if (anchorIS) *anchorIS = plex->anchorIS;
9611   PetscFunctionReturn(0);
9612 }
9613 
9614 /*@
9615   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.  Unlike boundary conditions,
9616   when a point's degrees of freedom in a section are constrained to an outside value, the anchor constraints set a
9617   point's degrees of freedom to be a linear combination of other points' degrees of freedom.
9618 
9619   After specifying the layout of constraints with DMPlexSetAnchors(), one specifies the constraints by calling
9620   DMGetDefaultConstraints() and filling in the entries in the constraint matrix.
9621 
9622   collective on dm
9623 
9624   Input Parameters:
9625 + dm - The DMPlex object
9626 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.  Must have a local communicator (PETSC_COMM_SELF or derivative).
9627 - anchorIS - The list of all anchor points.  Must have a local communicator (PETSC_COMM_SELF or derivative).
9628 
9629   The reference counts of anchorSection and anchorIS are incremented.
9630 
9631   Level: intermediate
9632 
9633 .seealso: `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9634 @*/
9635 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
9636 {
9637   DM_Plex    *plex = (DM_Plex *)dm->data;
9638   PetscMPIInt result;
9639 
9640   PetscFunctionBegin;
9641   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9642   if (anchorSection) {
9643     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
9644     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
9645     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
9646   }
9647   if (anchorIS) {
9648     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
9649     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
9650     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
9651   }
9652 
9653   PetscCall(PetscObjectReference((PetscObject)anchorSection));
9654   PetscCall(PetscSectionDestroy(&plex->anchorSection));
9655   plex->anchorSection = anchorSection;
9656 
9657   PetscCall(PetscObjectReference((PetscObject)anchorIS));
9658   PetscCall(ISDestroy(&plex->anchorIS));
9659   plex->anchorIS = anchorIS;
9660 
9661   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
9662     PetscInt        size, a, pStart, pEnd;
9663     const PetscInt *anchors;
9664 
9665     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9666     PetscCall(ISGetLocalSize(anchorIS, &size));
9667     PetscCall(ISGetIndices(anchorIS, &anchors));
9668     for (a = 0; a < size; a++) {
9669       PetscInt p;
9670 
9671       p = anchors[a];
9672       if (p >= pStart && p < pEnd) {
9673         PetscInt dof;
9674 
9675         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9676         if (dof) {
9677           PetscCall(ISRestoreIndices(anchorIS, &anchors));
9678           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
9679         }
9680       }
9681     }
9682     PetscCall(ISRestoreIndices(anchorIS, &anchors));
9683   }
9684   /* reset the generic constraints */
9685   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
9686   PetscFunctionReturn(0);
9687 }
9688 
9689 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
9690 {
9691   PetscSection anchorSection;
9692   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
9693 
9694   PetscFunctionBegin;
9695   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9696   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
9697   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
9698   PetscCall(PetscSectionGetNumFields(section, &numFields));
9699   if (numFields) {
9700     PetscInt f;
9701     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
9702 
9703     for (f = 0; f < numFields; f++) {
9704       PetscInt numComp;
9705 
9706       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
9707       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
9708     }
9709   }
9710   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9711   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9712   pStart = PetscMax(pStart, sStart);
9713   pEnd   = PetscMin(pEnd, sEnd);
9714   pEnd   = PetscMax(pStart, pEnd);
9715   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
9716   for (p = pStart; p < pEnd; p++) {
9717     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9718     if (dof) {
9719       PetscCall(PetscSectionGetDof(section, p, &dof));
9720       PetscCall(PetscSectionSetDof(*cSec, p, dof));
9721       for (f = 0; f < numFields; f++) {
9722         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
9723         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
9724       }
9725     }
9726   }
9727   PetscCall(PetscSectionSetUp(*cSec));
9728   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
9729   PetscFunctionReturn(0);
9730 }
9731 
9732 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
9733 {
9734   PetscSection    aSec;
9735   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
9736   const PetscInt *anchors;
9737   PetscInt        numFields, f;
9738   IS              aIS;
9739   MatType         mtype;
9740   PetscBool       iscuda, iskokkos;
9741 
9742   PetscFunctionBegin;
9743   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9744   PetscCall(PetscSectionGetStorageSize(cSec, &m));
9745   PetscCall(PetscSectionGetStorageSize(section, &n));
9746   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
9747   PetscCall(MatSetSizes(*cMat, m, n, m, n));
9748   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
9749   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
9750   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
9751   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
9752   if (iscuda) mtype = MATSEQAIJCUSPARSE;
9753   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
9754   else mtype = MATSEQAIJ;
9755   PetscCall(MatSetType(*cMat, mtype));
9756   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
9757   PetscCall(ISGetIndices(aIS, &anchors));
9758   /* cSec will be a subset of aSec and section */
9759   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
9760   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9761   PetscCall(PetscMalloc1(m + 1, &i));
9762   i[0] = 0;
9763   PetscCall(PetscSectionGetNumFields(section, &numFields));
9764   for (p = pStart; p < pEnd; p++) {
9765     PetscInt rDof, rOff, r;
9766 
9767     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9768     if (!rDof) continue;
9769     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9770     if (numFields) {
9771       for (f = 0; f < numFields; f++) {
9772         annz = 0;
9773         for (r = 0; r < rDof; r++) {
9774           a = anchors[rOff + r];
9775           if (a < sStart || a >= sEnd) continue;
9776           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
9777           annz += aDof;
9778         }
9779         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
9780         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
9781         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
9782       }
9783     } else {
9784       annz = 0;
9785       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9786       for (q = 0; q < dof; q++) {
9787         a = anchors[rOff + q];
9788         if (a < sStart || a >= sEnd) continue;
9789         PetscCall(PetscSectionGetDof(section, a, &aDof));
9790         annz += aDof;
9791       }
9792       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9793       PetscCall(PetscSectionGetOffset(cSec, p, &off));
9794       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
9795     }
9796   }
9797   nnz = i[m];
9798   PetscCall(PetscMalloc1(nnz, &j));
9799   offset = 0;
9800   for (p = pStart; p < pEnd; p++) {
9801     if (numFields) {
9802       for (f = 0; f < numFields; f++) {
9803         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
9804         for (q = 0; q < dof; q++) {
9805           PetscInt rDof, rOff, r;
9806           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9807           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9808           for (r = 0; r < rDof; r++) {
9809             PetscInt s;
9810 
9811             a = anchors[rOff + r];
9812             if (a < sStart || a >= sEnd) continue;
9813             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
9814             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
9815             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
9816           }
9817         }
9818       }
9819     } else {
9820       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9821       for (q = 0; q < dof; q++) {
9822         PetscInt rDof, rOff, r;
9823         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9824         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9825         for (r = 0; r < rDof; r++) {
9826           PetscInt s;
9827 
9828           a = anchors[rOff + r];
9829           if (a < sStart || a >= sEnd) continue;
9830           PetscCall(PetscSectionGetDof(section, a, &aDof));
9831           PetscCall(PetscSectionGetOffset(section, a, &aOff));
9832           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
9833         }
9834       }
9835     }
9836   }
9837   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
9838   PetscCall(PetscFree(i));
9839   PetscCall(PetscFree(j));
9840   PetscCall(ISRestoreIndices(aIS, &anchors));
9841   PetscFunctionReturn(0);
9842 }
9843 
9844 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
9845 {
9846   DM_Plex     *plex = (DM_Plex *)dm->data;
9847   PetscSection anchorSection, section, cSec;
9848   Mat          cMat;
9849 
9850   PetscFunctionBegin;
9851   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9852   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
9853   if (anchorSection) {
9854     PetscInt Nf;
9855 
9856     PetscCall(DMGetLocalSection(dm, &section));
9857     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
9858     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
9859     PetscCall(DMGetNumFields(dm, &Nf));
9860     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
9861     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
9862     PetscCall(PetscSectionDestroy(&cSec));
9863     PetscCall(MatDestroy(&cMat));
9864   }
9865   PetscFunctionReturn(0);
9866 }
9867 
9868 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
9869 {
9870   IS           subis;
9871   PetscSection section, subsection;
9872 
9873   PetscFunctionBegin;
9874   PetscCall(DMGetLocalSection(dm, &section));
9875   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
9876   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
9877   /* Create subdomain */
9878   PetscCall(DMPlexFilter(dm, label, value, subdm));
9879   /* Create submodel */
9880   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
9881   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
9882   PetscCall(DMSetLocalSection(*subdm, subsection));
9883   PetscCall(PetscSectionDestroy(&subsection));
9884   PetscCall(DMCopyDisc(dm, *subdm));
9885   /* Create map from submodel to global model */
9886   if (is) {
9887     PetscSection    sectionGlobal, subsectionGlobal;
9888     IS              spIS;
9889     const PetscInt *spmap;
9890     PetscInt       *subIndices;
9891     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
9892     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
9893 
9894     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
9895     PetscCall(ISGetIndices(spIS, &spmap));
9896     PetscCall(PetscSectionGetNumFields(section, &Nf));
9897     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
9898     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
9899     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
9900     for (p = pStart; p < pEnd; ++p) {
9901       PetscInt gdof, pSubSize = 0;
9902 
9903       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
9904       if (gdof > 0) {
9905         for (f = 0; f < Nf; ++f) {
9906           PetscInt fdof, fcdof;
9907 
9908           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
9909           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
9910           pSubSize += fdof - fcdof;
9911         }
9912         subSize += pSubSize;
9913         if (pSubSize) {
9914           if (bs < 0) {
9915             bs = pSubSize;
9916           } else if (bs != pSubSize) {
9917             /* Layout does not admit a pointwise block size */
9918             bs = 1;
9919           }
9920         }
9921       }
9922     }
9923     /* Must have same blocksize on all procs (some might have no points) */
9924     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
9925     bsLocal[1] = bs;
9926     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
9927     if (bsMinMax[0] != bsMinMax[1]) {
9928       bs = 1;
9929     } else {
9930       bs = bsMinMax[0];
9931     }
9932     PetscCall(PetscMalloc1(subSize, &subIndices));
9933     for (p = pStart; p < pEnd; ++p) {
9934       PetscInt gdof, goff;
9935 
9936       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
9937       if (gdof > 0) {
9938         const PetscInt point = spmap[p];
9939 
9940         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
9941         for (f = 0; f < Nf; ++f) {
9942           PetscInt fdof, fcdof, fc, f2, poff = 0;
9943 
9944           /* Can get rid of this loop by storing field information in the global section */
9945           for (f2 = 0; f2 < f; ++f2) {
9946             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
9947             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
9948             poff += fdof - fcdof;
9949           }
9950           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
9951           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
9952           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
9953         }
9954       }
9955     }
9956     PetscCall(ISRestoreIndices(spIS, &spmap));
9957     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
9958     if (bs > 1) {
9959       /* We need to check that the block size does not come from non-contiguous fields */
9960       PetscInt i, j, set = 1;
9961       for (i = 0; i < subSize; i += bs) {
9962         for (j = 0; j < bs; ++j) {
9963           if (subIndices[i + j] != subIndices[i] + j) {
9964             set = 0;
9965             break;
9966           }
9967         }
9968       }
9969       if (set) PetscCall(ISSetBlockSize(*is, bs));
9970     }
9971     /* Attach nullspace */
9972     for (f = 0; f < Nf; ++f) {
9973       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
9974       if ((*subdm)->nullspaceConstructors[f]) break;
9975     }
9976     if (f < Nf) {
9977       MatNullSpace nullSpace;
9978       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
9979 
9980       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
9981       PetscCall(MatNullSpaceDestroy(&nullSpace));
9982     }
9983   }
9984   PetscFunctionReturn(0);
9985 }
9986 
9987 /*@
9988   DMPlexMonitorThroughput - Report the cell throughput of FE integration
9989 
9990   Input Parameter:
9991 - dm - The DM
9992 
9993   Level: developer
9994 
9995   Options Database Keys:
9996 . -dm_plex_monitor_throughput - Activate the monitor
9997 
9998 .seealso: `DMSetFromOptions()`, `DMPlexCreate()`
9999 @*/
10000 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10001 {
10002 #if defined(PETSC_USE_LOG)
10003   PetscStageLog      stageLog;
10004   PetscLogEvent      event;
10005   PetscLogStage      stage;
10006   PetscEventPerfInfo eventInfo;
10007   PetscReal          cellRate, flopRate;
10008   PetscInt           cStart, cEnd, Nf, N;
10009   const char        *name;
10010 #endif
10011 
10012   PetscFunctionBegin;
10013   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10014 #if defined(PETSC_USE_LOG)
10015   PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10016   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10017   PetscCall(DMGetNumFields(dm, &Nf));
10018   PetscCall(PetscLogGetStageLog(&stageLog));
10019   PetscCall(PetscStageLogGetCurrent(stageLog, &stage));
10020   PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10021   PetscCall(PetscLogEventGetPerfInfo(stage, event, &eventInfo));
10022   N        = (cEnd - cStart) * Nf * eventInfo.count;
10023   flopRate = eventInfo.flops / eventInfo.time;
10024   cellRate = N / eventInfo.time;
10025   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, (double)cellRate, (double)(flopRate / 1.e6)));
10026 #else
10027   SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Plex Throughput Monitor is not supported if logging is turned off. Reconfigure using --with-log.");
10028 #endif
10029   PetscFunctionReturn(0);
10030 }
10031