xref: /petsc/src/dm/impls/plex/plex.c (revision 53134ebed6238e4143779c3e5c7db66beceae3ba)
1 #include <petsc/private/dmpleximpl.h>   /*I      "petscdmplex.h"   I*/
2 #include <petsc/private/isimpl.h>
3 #include <petsc/private/vecimpl.h>
4 #include <petsc/private/glvisvecimpl.h>
5 #include <petscsf.h>
6 #include <petscds.h>
7 #include <petscdraw.h>
8 #include <petscdmfield.h>
9 #include <petscdmplextransform.h>
10 
11 /* Logging support */
12 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;
13 PetscLogEvent DMPLEX_RebalBuildGraph,DMPLEX_RebalRewriteSF,DMPLEX_RebalGatherGraph, DMPLEX_RebalPartition, DMPLEX_RebalScatterPart;
14 
15 PETSC_EXTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
16 
17 /*@
18   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
19 
20   Input Parameter:
21 . dm      - The DMPlex object
22 
23   Output Parameter:
24 . simplex - Flag checking for a simplex
25 
26   Note: This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
27   If the mesh has no cells, this returns PETSC_FALSE.
28 
29   Level: intermediate
30 
31 .seealso `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
32 @*/
33 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
34 {
35   DMPolytopeType ct;
36   PetscInt       cStart, cEnd;
37 
38   PetscFunctionBegin;
39   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
40   if (cEnd <= cStart) {*simplex = PETSC_FALSE; PetscFunctionReturn(0);}
41   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
42   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct)+1 ? PETSC_TRUE : PETSC_FALSE;
43   PetscFunctionReturn(0);
44 }
45 
46 /*@
47   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
48 
49   Input Parameters:
50 + dm     - The DMPlex object
51 - height - The cell height in the Plex, 0 is the default
52 
53   Output Parameters:
54 + cStart - The first "normal" cell
55 - cEnd   - The upper bound on "normal"" cells
56 
57   Note: This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
58 
59   Level: developer
60 
61 .seealso `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
62 @*/
63 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd)
64 {
65   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
66   PetscInt       cS, cE, c;
67 
68   PetscFunctionBegin;
69   PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), &cS, &cE));
70   for (c = cS; c < cE; ++c) {
71     DMPolytopeType cct;
72 
73     PetscCall(DMPlexGetCellType(dm, c, &cct));
74     if ((PetscInt) cct < 0) break;
75     switch (cct) {
76       case DM_POLYTOPE_POINT:
77       case DM_POLYTOPE_SEGMENT:
78       case DM_POLYTOPE_TRIANGLE:
79       case DM_POLYTOPE_QUADRILATERAL:
80       case DM_POLYTOPE_TETRAHEDRON:
81       case DM_POLYTOPE_HEXAHEDRON:
82         ct = cct;
83         break;
84       default: break;
85     }
86     if (ct != DM_POLYTOPE_UNKNOWN) break;
87   }
88   if (ct != DM_POLYTOPE_UNKNOWN) {
89     DMLabel ctLabel;
90 
91     PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
92     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &cS, &cE));
93   }
94   if (cStart) *cStart = cS;
95   if (cEnd)   *cEnd   = cE;
96   PetscFunctionReturn(0);
97 }
98 
99 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
100 {
101   PetscInt       cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
102   PetscInt       vcdof[2] = {0,0}, globalvcdof[2];
103 
104   PetscFunctionBegin;
105   *ft  = PETSC_VTK_INVALID;
106   PetscCall(DMGetCoordinateDim(dm, &cdim));
107   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
108   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
109   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
110   if (field >= 0) {
111     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
112     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
113   } else {
114     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
115     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
116   }
117   PetscCallMPI(MPI_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
118   if (globalvcdof[0]) {
119     *sStart = vStart;
120     *sEnd   = vEnd;
121     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
122     else                        *ft = PETSC_VTK_POINT_FIELD;
123   } else if (globalvcdof[1]) {
124     *sStart = cStart;
125     *sEnd   = cEnd;
126     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
127     else                        *ft = PETSC_VTK_CELL_FIELD;
128   } else {
129     if (field >= 0) {
130       const char *fieldname;
131 
132       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
133       PetscCall(PetscInfo((PetscObject) dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
134     } else {
135       PetscCall(PetscInfo((PetscObject) dm, "Could not classify VTK output type of section\n"));
136     }
137   }
138   PetscFunctionReturn(0);
139 }
140 
141 /*@
142   DMPlexVecView1D - Plot many 1D solutions on the same line graph
143 
144   Collective on dm
145 
146   Input Parameters:
147 + dm - The DMPlex
148 . n  - The number of vectors
149 . u  - The array of local vectors
150 - viewer - The Draw viewer
151 
152   Level: advanced
153 
154 .seealso: `VecViewFromOptions()`, `VecView()`
155 @*/
156 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
157 {
158   PetscDS            ds;
159   PetscDraw          draw = NULL;
160   PetscDrawLG        lg;
161   Vec                coordinates;
162   const PetscScalar *coords, **sol;
163   PetscReal         *vals;
164   PetscInt          *Nc;
165   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
166   char             **names;
167 
168   PetscFunctionBegin;
169   PetscCall(DMGetDS(dm, &ds));
170   PetscCall(PetscDSGetNumFields(ds, &Nf));
171   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
172   PetscCall(PetscDSGetComponents(ds, &Nc));
173 
174   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
175   if (!draw) PetscFunctionReturn(0);
176   PetscCall(PetscDrawLGCreate(draw, n*Nl, &lg));
177 
178   PetscCall(PetscMalloc3(n, &sol, n*Nl, &names, n*Nl, &vals));
179   for (i = 0, l = 0; i < n; ++i) {
180     const char *vname;
181 
182     PetscCall(PetscObjectGetName((PetscObject) u[i], &vname));
183     for (f = 0; f < Nf; ++f) {
184       PetscObject disc;
185       const char *fname;
186       char        tmpname[PETSC_MAX_PATH_LEN];
187 
188       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
189       /* TODO Create names for components */
190       for (c = 0; c < Nc[f]; ++c, ++l) {
191         PetscCall(PetscObjectGetName(disc, &fname));
192         PetscCall(PetscStrcpy(tmpname, vname));
193         PetscCall(PetscStrlcat(tmpname, ":", PETSC_MAX_PATH_LEN));
194         PetscCall(PetscStrlcat(tmpname, fname, PETSC_MAX_PATH_LEN));
195         PetscCall(PetscStrallocpy(tmpname, &names[l]));
196       }
197     }
198   }
199   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *) names));
200   /* Just add P_1 support for now */
201   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
202   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
203   PetscCall(VecGetArrayRead(coordinates, &coords));
204   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
205   for (v = vStart; v < vEnd; ++v) {
206     PetscScalar *x, *svals;
207 
208     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
209     for (i = 0; i < n; ++i) {
210       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
211       for (l = 0; l < Nl; ++l) vals[i*Nl + l] = PetscRealPart(svals[l]);
212     }
213     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
214   }
215   PetscCall(VecRestoreArrayRead(coordinates, &coords));
216   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
217   for (l = 0; l < n*Nl; ++l) PetscCall(PetscFree(names[l]));
218   PetscCall(PetscFree3(sol, names, vals));
219 
220   PetscCall(PetscDrawLGDraw(lg));
221   PetscCall(PetscDrawLGDestroy(&lg));
222   PetscFunctionReturn(0);
223 }
224 
225 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
226 {
227   DM             dm;
228 
229   PetscFunctionBegin;
230   PetscCall(VecGetDM(u, &dm));
231   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
232   PetscFunctionReturn(0);
233 }
234 
235 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
236 {
237   DM                 dm;
238   PetscSection       s;
239   PetscDraw          draw, popup;
240   DM                 cdm;
241   PetscSection       coordSection;
242   Vec                coordinates;
243   const PetscScalar *coords, *array;
244   PetscReal          bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
245   PetscReal          vbound[2], time;
246   PetscBool          flg;
247   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
248   const char        *name;
249   char               title[PETSC_MAX_PATH_LEN];
250 
251   PetscFunctionBegin;
252   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
253   PetscCall(VecGetDM(v, &dm));
254   PetscCall(DMGetCoordinateDim(dm, &dim));
255   PetscCall(DMGetLocalSection(dm, &s));
256   PetscCall(PetscSectionGetNumFields(s, &Nf));
257   PetscCall(DMGetCoarsenLevel(dm, &level));
258   PetscCall(DMGetCoordinateDM(dm, &cdm));
259   PetscCall(DMGetLocalSection(cdm, &coordSection));
260   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
261   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
262   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
263 
264   PetscCall(PetscObjectGetName((PetscObject) v, &name));
265   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
266 
267   PetscCall(VecGetLocalSize(coordinates, &N));
268   PetscCall(VecGetArrayRead(coordinates, &coords));
269   for (c = 0; c < N; c += dim) {
270     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));   bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
271     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c+1])); bound[3] = PetscMax(bound[3], PetscRealPart(coords[c+1]));
272   }
273   PetscCall(VecRestoreArrayRead(coordinates, &coords));
274   PetscCall(PetscDrawClear(draw));
275 
276   /* Could implement something like DMDASelectFields() */
277   for (f = 0; f < Nf; ++f) {
278     DM   fdm = dm;
279     Vec  fv  = v;
280     IS   fis;
281     char prefix[PETSC_MAX_PATH_LEN];
282     const char *fname;
283 
284     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
285     PetscCall(PetscSectionGetFieldName(s, f, &fname));
286 
287     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix,sizeof(prefix)));
288     else               {prefix[0] = '\0';}
289     if (Nf > 1) {
290       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
291       PetscCall(VecGetSubVector(v, fis, &fv));
292       PetscCall(PetscStrlcat(prefix, fname,sizeof(prefix)));
293       PetscCall(PetscStrlcat(prefix, "_",sizeof(prefix)));
294     }
295     for (comp = 0; comp < Nc; ++comp, ++w) {
296       PetscInt nmax = 2;
297 
298       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
299       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
300       else        PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
301       PetscCall(PetscDrawSetTitle(draw, title));
302 
303       /* TODO Get max and min only for this component */
304       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
305       if (!flg) {
306         PetscCall(VecMin(fv, NULL, &vbound[0]));
307         PetscCall(VecMax(fv, NULL, &vbound[1]));
308         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
309       }
310       PetscCall(PetscDrawGetPopup(draw, &popup));
311       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
312       PetscCall(PetscDrawSetCoordinates(draw, bound[0], bound[1], bound[2], bound[3]));
313 
314       PetscCall(VecGetArrayRead(fv, &array));
315       for (c = cStart; c < cEnd; ++c) {
316         PetscScalar *coords = NULL, *a = NULL;
317         PetscInt     numCoords, color[4] = {-1,-1,-1,-1};
318 
319         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
320         if (a) {
321           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
322           color[1] = color[2] = color[3] = color[0];
323         } else {
324           PetscScalar *vals = NULL;
325           PetscInt     numVals, va;
326 
327           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
328           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);
329           switch (numVals/Nc) {
330           case 3: /* P1 Triangle */
331           case 4: /* P1 Quadrangle */
332             for (va = 0; va < numVals/Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va*Nc+comp]), vbound[0], vbound[1]);
333             break;
334           case 6: /* P2 Triangle */
335           case 8: /* P2 Quadrangle */
336             for (va = 0; va < numVals/(Nc*2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va*Nc+comp + numVals/(Nc*2)]), vbound[0], vbound[1]);
337             break;
338           default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals/Nc);
339           }
340           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
341         }
342         PetscCall(DMPlexVecGetClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
343         switch (numCoords) {
344         case 6:
345         case 12: /* Localized triangle */
346           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]));
347           break;
348         case 8:
349         case 16: /* Localized quadrilateral */
350           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]));
351           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]));
352           break;
353         default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
354         }
355         PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
356       }
357       PetscCall(VecRestoreArrayRead(fv, &array));
358       PetscCall(PetscDrawFlush(draw));
359       PetscCall(PetscDrawPause(draw));
360       PetscCall(PetscDrawSave(draw));
361     }
362     if (Nf > 1) {
363       PetscCall(VecRestoreSubVector(v, fis, &fv));
364       PetscCall(ISDestroy(&fis));
365       PetscCall(DMDestroy(&fdm));
366     }
367   }
368   PetscFunctionReturn(0);
369 }
370 
371 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
372 {
373   DM        dm;
374   PetscDraw draw;
375   PetscInt  dim;
376   PetscBool isnull;
377 
378   PetscFunctionBegin;
379   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
380   PetscCall(PetscDrawIsNull(draw, &isnull));
381   if (isnull) PetscFunctionReturn(0);
382 
383   PetscCall(VecGetDM(v, &dm));
384   PetscCall(DMGetCoordinateDim(dm, &dim));
385   switch (dim) {
386   case 1: PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));break;
387   case 2: PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));break;
388   default: SETERRQ(PetscObjectComm((PetscObject) v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
389   }
390   PetscFunctionReturn(0);
391 }
392 
393 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
394 {
395   DM                      dm;
396   Vec                     locv;
397   const char              *name;
398   PetscSection            section;
399   PetscInt                pStart, pEnd;
400   PetscInt                numFields;
401   PetscViewerVTKFieldType ft;
402 
403   PetscFunctionBegin;
404   PetscCall(VecGetDM(v, &dm));
405   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
406   PetscCall(PetscObjectGetName((PetscObject) v, &name));
407   PetscCall(PetscObjectSetName((PetscObject) locv, name));
408   PetscCall(VecCopy(v, locv));
409   PetscCall(DMGetLocalSection(dm, &section));
410   PetscCall(PetscSectionGetNumFields(section, &numFields));
411   if (!numFields) {
412     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
413     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject) dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE,(PetscObject) locv));
414   } else {
415     PetscInt f;
416 
417     for (f = 0; f < numFields; f++) {
418       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
419       if (ft == PETSC_VTK_INVALID) continue;
420       PetscCall(PetscObjectReference((PetscObject)locv));
421       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject) dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE,(PetscObject) locv));
422     }
423     PetscCall(VecDestroy(&locv));
424   }
425   PetscFunctionReturn(0);
426 }
427 
428 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
429 {
430   DM             dm;
431   PetscBool      isvtk, ishdf5, isdraw, isglvis;
432 
433   PetscFunctionBegin;
434   PetscCall(VecGetDM(v, &dm));
435   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
436   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,   &isvtk));
437   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,  &ishdf5));
438   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERDRAW,  &isdraw));
439   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERGLVIS, &isglvis));
440   if (isvtk || ishdf5 || isdraw || isglvis) {
441     PetscInt    i,numFields;
442     PetscObject fe;
443     PetscBool   fem = PETSC_FALSE;
444     Vec         locv = v;
445     const char  *name;
446     PetscInt    step;
447     PetscReal   time;
448 
449     PetscCall(DMGetNumFields(dm, &numFields));
450     for (i=0; i<numFields; i++) {
451       PetscCall(DMGetField(dm, i, NULL, &fe));
452       if (fe->classid == PETSCFE_CLASSID) { fem = PETSC_TRUE; break; }
453     }
454     if (fem) {
455       PetscObject isZero;
456 
457       PetscCall(DMGetLocalVector(dm, &locv));
458       PetscCall(PetscObjectGetName((PetscObject) v, &name));
459       PetscCall(PetscObjectSetName((PetscObject) locv, name));
460       PetscCall(PetscObjectQuery((PetscObject) v, "__Vec_bc_zero__", &isZero));
461       PetscCall(PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", isZero));
462       PetscCall(VecCopy(v, locv));
463       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
464       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
465     }
466     if (isvtk) {
467       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
468     } else if (ishdf5) {
469 #if defined(PETSC_HAVE_HDF5)
470       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
471 #else
472       SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
473 #endif
474     } else if (isdraw) {
475       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
476     } else if (isglvis) {
477       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
478       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
479       PetscCall(VecView_GLVis(locv, viewer));
480     }
481     if (fem) {
482       PetscCall(PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", NULL));
483       PetscCall(DMRestoreLocalVector(dm, &locv));
484     }
485   } else {
486     PetscBool isseq;
487 
488     PetscCall(PetscObjectTypeCompare((PetscObject) v, VECSEQ, &isseq));
489     if (isseq) PetscCall(VecView_Seq(v, viewer));
490     else       PetscCall(VecView_MPI(v, viewer));
491   }
492   PetscFunctionReturn(0);
493 }
494 
495 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
496 {
497   DM        dm;
498   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii;
499 
500   PetscFunctionBegin;
501   PetscCall(VecGetDM(v, &dm));
502   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
503   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,      &isvtk));
504   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,     &ishdf5));
505   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERDRAW,     &isdraw));
506   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERGLVIS,    &isglvis));
507   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWEREXODUSII, &isexodusii));
508   if (isvtk || isdraw || isglvis) {
509     Vec         locv;
510     PetscObject isZero;
511     const char *name;
512 
513     PetscCall(DMGetLocalVector(dm, &locv));
514     PetscCall(PetscObjectGetName((PetscObject) v, &name));
515     PetscCall(PetscObjectSetName((PetscObject) locv, name));
516     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
517     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
518     PetscCall(PetscObjectQuery((PetscObject) v, "__Vec_bc_zero__", &isZero));
519     PetscCall(PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", isZero));
520     PetscCall(VecView_Plex_Local(locv, viewer));
521     PetscCall(PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", NULL));
522     PetscCall(DMRestoreLocalVector(dm, &locv));
523   } else if (ishdf5) {
524 #if defined(PETSC_HAVE_HDF5)
525     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
526 #else
527     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
528 #endif
529   } else if (isexodusii) {
530 #if defined(PETSC_HAVE_EXODUSII)
531     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
532 #else
533     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
534 #endif
535   } else {
536     PetscBool isseq;
537 
538     PetscCall(PetscObjectTypeCompare((PetscObject) v, VECSEQ, &isseq));
539     if (isseq) PetscCall(VecView_Seq(v, viewer));
540     else       PetscCall(VecView_MPI(v, viewer));
541   }
542   PetscFunctionReturn(0);
543 }
544 
545 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
546 {
547   DM                dm;
548   MPI_Comm          comm;
549   PetscViewerFormat format;
550   Vec               v;
551   PetscBool         isvtk, ishdf5;
552 
553   PetscFunctionBegin;
554   PetscCall(VecGetDM(originalv, &dm));
555   PetscCall(PetscObjectGetComm((PetscObject) originalv, &comm));
556   PetscCheck(dm,comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
557   PetscCall(PetscViewerGetFormat(viewer, &format));
558   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
559   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,  &isvtk));
560   if (format == PETSC_VIEWER_NATIVE) {
561     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
562     /* this need a better fix */
563     if (dm->useNatural) {
564       if (dm->sfNatural) {
565         const char *vecname;
566         PetscInt    n, nroots;
567 
568         PetscCall(VecGetLocalSize(originalv, &n));
569         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
570         if (n == nroots) {
571           PetscCall(DMGetGlobalVector(dm, &v));
572           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
573           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
574           PetscCall(PetscObjectGetName((PetscObject) originalv, &vecname));
575           PetscCall(PetscObjectSetName((PetscObject) v, vecname));
576         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
577       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
578     } else v = originalv;
579   } else v = originalv;
580 
581   if (ishdf5) {
582 #if defined(PETSC_HAVE_HDF5)
583     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
584 #else
585     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
586 #endif
587   } else if (isvtk) {
588     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
589   } else {
590     PetscBool isseq;
591 
592     PetscCall(PetscObjectTypeCompare((PetscObject) v, VECSEQ, &isseq));
593     if (isseq) PetscCall(VecView_Seq(v, viewer));
594     else       PetscCall(VecView_MPI(v, viewer));
595   }
596   if (v != originalv) PetscCall(DMRestoreGlobalVector(dm, &v));
597   PetscFunctionReturn(0);
598 }
599 
600 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
601 {
602   DM             dm;
603   PetscBool      ishdf5;
604 
605   PetscFunctionBegin;
606   PetscCall(VecGetDM(v, &dm));
607   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
608   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
609   if (ishdf5) {
610     DM          dmBC;
611     Vec         gv;
612     const char *name;
613 
614     PetscCall(DMGetOutputDM(dm, &dmBC));
615     PetscCall(DMGetGlobalVector(dmBC, &gv));
616     PetscCall(PetscObjectGetName((PetscObject) v, &name));
617     PetscCall(PetscObjectSetName((PetscObject) gv, name));
618     PetscCall(VecLoad_Default(gv, viewer));
619     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
620     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
621     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
622   } else PetscCall(VecLoad_Default(v, viewer));
623   PetscFunctionReturn(0);
624 }
625 
626 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
627 {
628   DM             dm;
629   PetscBool      ishdf5,isexodusii;
630 
631   PetscFunctionBegin;
632   PetscCall(VecGetDM(v, &dm));
633   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
634   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,     &ishdf5));
635   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWEREXODUSII, &isexodusii));
636   if (ishdf5) {
637 #if defined(PETSC_HAVE_HDF5)
638     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
639 #else
640     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
641 #endif
642   } else if (isexodusii) {
643 #if defined(PETSC_HAVE_EXODUSII)
644     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
645 #else
646     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
647 #endif
648   } else PetscCall(VecLoad_Default(v, viewer));
649   PetscFunctionReturn(0);
650 }
651 
652 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
653 {
654   DM                dm;
655   PetscViewerFormat format;
656   PetscBool         ishdf5;
657 
658   PetscFunctionBegin;
659   PetscCall(VecGetDM(originalv, &dm));
660   PetscCheck(dm,PetscObjectComm((PetscObject) originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
661   PetscCall(PetscViewerGetFormat(viewer, &format));
662   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
663   if (format == PETSC_VIEWER_NATIVE) {
664     if (dm->useNatural) {
665       if (dm->sfNatural) {
666         if (ishdf5) {
667 #if defined(PETSC_HAVE_HDF5)
668           Vec         v;
669           const char *vecname;
670 
671           PetscCall(DMGetGlobalVector(dm, &v));
672           PetscCall(PetscObjectGetName((PetscObject) originalv, &vecname));
673           PetscCall(PetscObjectSetName((PetscObject) v, vecname));
674           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
675           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
676           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
677           PetscCall(DMRestoreGlobalVector(dm, &v));
678 #else
679           SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
680 #endif
681         } else SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
682       }
683     } else PetscCall(VecLoad_Default(originalv, viewer));
684   }
685   PetscFunctionReturn(0);
686 }
687 
688 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
689 {
690   PetscSection       coordSection;
691   Vec                coordinates;
692   DMLabel            depthLabel, celltypeLabel;
693   const char        *name[4];
694   const PetscScalar *a;
695   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
696 
697   PetscFunctionBegin;
698   PetscCall(DMGetDimension(dm, &dim));
699   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
700   PetscCall(DMGetCoordinateSection(dm, &coordSection));
701   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
702   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
703   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
704   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
705   PetscCall(VecGetArrayRead(coordinates, &a));
706   name[0]     = "vertex";
707   name[1]     = "edge";
708   name[dim-1] = "face";
709   name[dim]   = "cell";
710   for (c = cStart; c < cEnd; ++c) {
711     PetscInt *closure = NULL;
712     PetscInt  closureSize, cl, ct;
713 
714     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
715     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
716     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
717     PetscCall(PetscViewerASCIIPushTab(viewer));
718     for (cl = 0; cl < closureSize*2; cl += 2) {
719       PetscInt point = closure[cl], depth, dof, off, d, p;
720 
721       if ((point < pStart) || (point >= pEnd)) continue;
722       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
723       if (!dof) continue;
724       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
725       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
726       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
727       for (p = 0; p < dof/dim; ++p) {
728         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
729         for (d = 0; d < dim; ++d) {
730           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
731           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double) PetscRealPart(a[off+p*dim+d])));
732         }
733         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
734       }
735       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
736     }
737     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
738     PetscCall(PetscViewerASCIIPopTab(viewer));
739   }
740   PetscCall(VecRestoreArrayRead(coordinates, &a));
741   PetscFunctionReturn(0);
742 }
743 
744 typedef enum {CS_CARTESIAN, CS_POLAR, CS_CYLINDRICAL, CS_SPHERICAL} CoordSystem;
745 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
746 
747 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
748 {
749   PetscInt       i;
750 
751   PetscFunctionBegin;
752   if (dim > 3) {
753     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double) PetscRealPart(x[i])));
754   } else {
755     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
756 
757     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
758     switch (cs) {
759       case CS_CARTESIAN: for (i = 0; i < dim; ++i) trcoords[i] = coords[i];break;
760       case CS_POLAR:
761         PetscCheck(dim == 2,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
762         trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
763         trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
764         break;
765       case CS_CYLINDRICAL:
766         PetscCheck(dim == 3,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
767         trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
768         trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
769         trcoords[2] = coords[2];
770         break;
771       case CS_SPHERICAL:
772         PetscCheck(dim == 3,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
773         trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
774         trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
775         trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
776         break;
777     }
778     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double) trcoords[i]));
779   }
780   PetscFunctionReturn(0);
781 }
782 
783 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
784 {
785   DM_Plex          *mesh = (DM_Plex*) dm->data;
786   DM                cdm, cdmCell;
787   PetscSection      coordSection, coordSectionCell;
788   Vec               coordinates, coordinatesCell;
789   PetscViewerFormat format;
790 
791   PetscFunctionBegin;
792   PetscCall(DMGetCoordinateDM(dm, &cdm));
793   PetscCall(DMGetCoordinateSection(dm, &coordSection));
794   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
795   PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
796   PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
797   PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
798   PetscCall(PetscViewerGetFormat(viewer, &format));
799   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
800     const char *name;
801     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
802     PetscInt    pStart, pEnd, p, numLabels, l;
803     PetscMPIInt rank, size;
804 
805     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
806     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
807     PetscCall(PetscObjectGetName((PetscObject) dm, &name));
808     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
809     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
810     PetscCall(DMGetDimension(dm, &dim));
811     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
812     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
813     else      PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
814     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
815     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
816     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
817     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
818     for (p = pStart; p < pEnd; ++p) {
819       PetscInt dof, off, s;
820 
821       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
822       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
823       for (s = off; s < off+dof; ++s) {
824         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
825       }
826     }
827     PetscCall(PetscViewerFlush(viewer));
828     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
829     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
830     for (p = pStart; p < pEnd; ++p) {
831       PetscInt dof, off, c;
832 
833       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
834       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
835       for (c = off; c < off+dof; ++c) {
836         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " <---- %" PetscInt_FMT " (%" PetscInt_FMT ")\n", rank, p, mesh->cones[c], mesh->coneOrientations[c]));
837       }
838     }
839     PetscCall(PetscViewerFlush(viewer));
840     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
841     if (coordSection && coordinates) {
842       CoordSystem        cs = CS_CARTESIAN;
843       const PetscScalar *array, *arrayCell = NULL;
844       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
845       PetscMPIInt        rank;
846       const char        *name;
847 
848       PetscCall(PetscOptionsGetEnum(((PetscObject) viewer)->options, ((PetscObject) viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *) &cs, NULL));
849       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
850       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
851       PetscCheck(Nf == 1,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
852       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
853       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
854       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
855       pStart =  PetscMin(pvStart, pcStart);
856       pEnd   =  PetscMax(pvEnd,   pcEnd);
857       PetscCall(PetscObjectGetName((PetscObject) coordinates, &name));
858       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
859       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
860       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
861 
862       PetscCall(VecGetArrayRead(coordinates, &array));
863       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
864       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
865       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
866       for (p = pStart; p < pEnd; ++p) {
867         PetscInt dof, off;
868 
869         if (p >= pvStart && p < pvEnd) {
870           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
871           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
872           if (dof) {
873             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
874             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
875             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
876           }
877         }
878         if (cdmCell && p >= pcStart && p < pcEnd) {
879           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
880           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
881           if (dof) {
882             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
883             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
884             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
885           }
886         }
887       }
888       PetscCall(PetscViewerFlush(viewer));
889       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
890       PetscCall(VecRestoreArrayRead(coordinates, &array));
891       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
892     }
893     PetscCall(DMGetNumLabels(dm, &numLabels));
894     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
895     for (l = 0; l < numLabels; ++l) {
896       DMLabel     label;
897       PetscBool   isdepth;
898       const char *name;
899 
900       PetscCall(DMGetLabelName(dm, l, &name));
901       PetscCall(PetscStrcmp(name, "depth", &isdepth));
902       if (isdepth) continue;
903       PetscCall(DMGetLabel(dm, name, &label));
904       PetscCall(DMLabelView(label, viewer));
905     }
906     if (size > 1) {
907       PetscSF sf;
908 
909       PetscCall(DMGetPointSF(dm, &sf));
910       PetscCall(PetscSFView(sf, viewer));
911     }
912     PetscCall(PetscViewerFlush(viewer));
913   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
914     const char  *name, *color;
915     const char  *defcolors[3]  = {"gray", "orange", "green"};
916     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
917     char         lname[PETSC_MAX_PATH_LEN];
918     PetscReal    scale         = 2.0;
919     PetscReal    tikzscale     = 1.0;
920     PetscBool    useNumbers    = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
921     double       tcoords[3];
922     PetscScalar *coords;
923     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
924     PetscMPIInt  rank, size;
925     char         **names, **colors, **lcolors;
926     PetscBool    flg, lflg;
927     PetscBT      wp = NULL;
928     PetscInt     pEnd, pStart;
929 
930     PetscCall(DMGetDimension(dm, &dim));
931     PetscCall(DMPlexGetDepth(dm, &depth));
932     PetscCall(DMGetNumLabels(dm, &numLabels));
933     numLabels  = PetscMax(numLabels, 10);
934     numColors  = 10;
935     numLColors = 10;
936     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
937     PetscCall(PetscOptionsGetReal(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
938     PetscCall(PetscOptionsGetReal(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
939     PetscCall(PetscOptionsGetBool(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
940     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
941     for (d = 0; d < 4; ++d) drawColors[d]  = PETSC_TRUE;
942     n = 4;
943     PetscCall(PetscOptionsGetBoolArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
944     PetscCheck(!flg || n == dim+1,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim+1);
945     PetscCall(PetscOptionsGetBoolArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
946     PetscCheck(!flg || n == dim+1,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim+1);
947     PetscCall(PetscOptionsGetStringArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
948     if (!useLabels) numLabels = 0;
949     PetscCall(PetscOptionsGetStringArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
950     if (!useColors) {
951       numColors = 3;
952       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
953     }
954     PetscCall(PetscOptionsGetStringArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
955     if (!useColors) {
956       numLColors = 4;
957       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
958     }
959     PetscCall(PetscOptionsGetString(((PetscObject) viewer)->options, ((PetscObject) viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
960     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
961     PetscCall(PetscOptionsGetBool(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
962     PetscCheck(!flg || !plotEdges || depth >= dim,PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Mesh must be interpolated");
963     if (depth < dim) plotEdges = PETSC_FALSE;
964     PetscCall(PetscOptionsGetBool(((PetscObject) viewer)->options, ((PetscObject) viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
965 
966     /* filter points with labelvalue != labeldefaultvalue */
967     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
968     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
969     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
970     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
971     if (lflg) {
972       DMLabel lbl;
973 
974       PetscCall(DMGetLabel(dm, lname, &lbl));
975       if (lbl) {
976         PetscInt val, defval;
977 
978         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
979         PetscCall(PetscBTCreate(pEnd-pStart, &wp));
980         for (c = pStart;  c < pEnd; c++) {
981           PetscInt *closure = NULL;
982           PetscInt  closureSize;
983 
984           PetscCall(DMLabelGetValue(lbl, c, &val));
985           if (val == defval) continue;
986 
987           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
988           for (p = 0; p < closureSize*2; p += 2) {
989             PetscCall(PetscBTSet(wp, closure[p] - pStart));
990           }
991           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
992         }
993       }
994     }
995 
996     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
997     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
998     PetscCall(PetscObjectGetName((PetscObject) dm, &name));
999     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1000 \\documentclass[tikz]{standalone}\n\n\
1001 \\usepackage{pgflibraryshapes}\n\
1002 \\usetikzlibrary{backgrounds}\n\
1003 \\usetikzlibrary{arrows}\n\
1004 \\begin{document}\n"));
1005     if (size > 1) {
1006       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1007       for (p = 0; p < size; ++p) {
1008         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size-1) ? ", and " :  ", "));
1009         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p%numColors], p));
1010       }
1011       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1012     }
1013     if (drawHasse) {
1014       PetscInt maxStratum = PetscMax(vEnd-vStart, PetscMax(eEnd-eStart, cEnd-cStart));
1015 
1016       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1017       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd-1));
1018       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd-vStart));
1019       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum-(vEnd-vStart))/2.));
1020       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1021       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd-1));
1022       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum-(eEnd-eStart))/2.));
1023       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd-eStart));
1024       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1025       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd-1));
1026       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd-cStart));
1027       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum-(cEnd-cStart))/2.));
1028     }
1029     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double) tikzscale));
1030 
1031     /* Plot vertices */
1032     PetscCall(VecGetArray(coordinates, &coords));
1033     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1034     for (v = vStart; v < vEnd; ++v) {
1035       PetscInt  off, dof, d;
1036       PetscBool isLabeled = PETSC_FALSE;
1037 
1038       if (wp && !PetscBTLookup(wp,v - pStart)) continue;
1039       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1040       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1041       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1042       PetscCheck(dof <= 3,PETSC_COMM_SELF,PETSC_ERR_PLIB,"coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3",v,dof);
1043       for (d = 0; d < dof; ++d) {
1044         tcoords[d] = (double) (scale*PetscRealPart(coords[off+d]));
1045         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1046       }
1047       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1048       if (dim == 3) {PetscReal tmp = tcoords[1]; tcoords[1] = tcoords[2]; tcoords[2] = -tmp;}
1049       for (d = 0; d < dof; ++d) {
1050         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1051         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double) tcoords[d]));
1052       }
1053       if (drawHasse) color = colors[0%numColors];
1054       else           color = colors[rank%numColors];
1055       for (l = 0; l < numLabels; ++l) {
1056         PetscInt val;
1057         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1058         if (val >= 0) {color = lcolors[l%numLColors]; isLabeled = PETSC_TRUE; break;}
1059       }
1060       if (drawNumbers[0]) {
1061         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1062       } else if (drawColors[0]) {
1063         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1064       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1065     }
1066     PetscCall(VecRestoreArray(coordinates, &coords));
1067     PetscCall(PetscViewerFlush(viewer));
1068     /* Plot edges */
1069     if (plotEdges) {
1070       PetscCall(VecGetArray(coordinates, &coords));
1071       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1072       for (e = eStart; e < eEnd; ++e) {
1073         const PetscInt *cone;
1074         PetscInt        coneSize, offA, offB, dof, d;
1075 
1076         if (wp && !PetscBTLookup(wp,e - pStart)) continue;
1077         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1078         PetscCheck(coneSize == 2,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1079         PetscCall(DMPlexGetCone(dm, e, &cone));
1080         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1081         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1082         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1083         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1084         for (d = 0; d < dof; ++d) {
1085           tcoords[d] = (double) (0.5*scale*PetscRealPart(coords[offA+d]+coords[offB+d]));
1086           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1087         }
1088         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1089         if (dim == 3) {PetscReal tmp = tcoords[1]; tcoords[1] = tcoords[2]; tcoords[2] = -tmp;}
1090         for (d = 0; d < dof; ++d) {
1091           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1092           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1093         }
1094         if (drawHasse) color = colors[1%numColors];
1095         else           color = colors[rank%numColors];
1096         for (l = 0; l < numLabels; ++l) {
1097           PetscInt val;
1098           PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1099           if (val >= 0) {color = lcolors[l%numLColors]; break;}
1100         }
1101         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1102       }
1103       PetscCall(VecRestoreArray(coordinates, &coords));
1104       PetscCall(PetscViewerFlush(viewer));
1105       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1106     }
1107     /* Plot cells */
1108     if (dim == 3 || !drawNumbers[1]) {
1109       for (e = eStart; e < eEnd; ++e) {
1110         const PetscInt *cone;
1111 
1112         if (wp && !PetscBTLookup(wp,e - pStart)) continue;
1113         color = colors[rank%numColors];
1114         for (l = 0; l < numLabels; ++l) {
1115           PetscInt val;
1116           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1117           if (val >= 0) {color = lcolors[l%numLColors]; break;}
1118         }
1119         PetscCall(DMPlexGetCone(dm, e, &cone));
1120         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1121       }
1122     } else {
1123        DMPolytopeType ct;
1124 
1125       /* Drawing a 2D polygon */
1126       for (c = cStart; c < cEnd; ++c) {
1127         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1128         PetscCall(DMPlexGetCellType(dm, c, &ct));
1129         if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR ||
1130             ct == DM_POLYTOPE_TRI_PRISM_TENSOR ||
1131             ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
1132           const PetscInt *cone;
1133           PetscInt        coneSize, e;
1134 
1135           PetscCall(DMPlexGetCone(dm, c, &cone));
1136           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1137           for (e = 0; e < coneSize; ++e) {
1138             const PetscInt *econe;
1139 
1140             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1141             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));
1142           }
1143         } else {
1144           PetscInt *closure = NULL;
1145           PetscInt  closureSize, Nv = 0, v;
1146 
1147           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1148           for (p = 0; p < closureSize*2; p += 2) {
1149             const PetscInt point = closure[p];
1150 
1151             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1152           }
1153           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank%numColors]));
1154           for (v = 0; v <= Nv; ++v) {
1155             const PetscInt vertex = closure[v%Nv];
1156 
1157             if (v > 0) {
1158               if (plotEdges) {
1159                 const PetscInt *edge;
1160                 PetscInt        endpoints[2], ne;
1161 
1162                 endpoints[0] = closure[v-1]; endpoints[1] = vertex;
1163                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1164                 PetscCheck(ne == 1,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1165                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1166                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1167               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1168             }
1169             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1170           }
1171           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1172           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1173         }
1174       }
1175     }
1176     PetscCall(VecGetArray(coordinates, &coords));
1177     for (c = cStart; c < cEnd; ++c) {
1178       double    ccoords[3] = {0.0, 0.0, 0.0};
1179       PetscBool isLabeled  = PETSC_FALSE;
1180       PetscInt *closure    = NULL;
1181       PetscInt  closureSize, dof, d, n = 0;
1182 
1183       if (wp && !PetscBTLookup(wp,c - pStart)) continue;
1184       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1185       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1186       for (p = 0; p < closureSize*2; p += 2) {
1187         const PetscInt point = closure[p];
1188         PetscInt       off;
1189 
1190         if ((point < vStart) || (point >= vEnd)) continue;
1191         PetscCall(PetscSectionGetDof(coordSection, point, &dof));
1192         PetscCall(PetscSectionGetOffset(coordSection, point, &off));
1193         for (d = 0; d < dof; ++d) {
1194           tcoords[d] = (double) (scale*PetscRealPart(coords[off+d]));
1195           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1196         }
1197         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1198         if (dof == 3) {PetscReal tmp = tcoords[1]; tcoords[1] = tcoords[2]; tcoords[2] = -tmp;}
1199         for (d = 0; d < dof; ++d) {ccoords[d] += tcoords[d];}
1200         ++n;
1201       }
1202       for (d = 0; d < dof; ++d) {ccoords[d] /= n;}
1203       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1204       for (d = 0; d < dof; ++d) {
1205         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1206         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double) ccoords[d]));
1207       }
1208       if (drawHasse) color = colors[depth%numColors];
1209       else           color = colors[rank%numColors];
1210       for (l = 0; l < numLabels; ++l) {
1211         PetscInt val;
1212         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1213         if (val >= 0) {color = lcolors[l%numLColors]; isLabeled = PETSC_TRUE; break;}
1214       }
1215       if (drawNumbers[dim]) {
1216         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1217       } else if (drawColors[dim]) {
1218         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1219       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1220     }
1221     PetscCall(VecRestoreArray(coordinates, &coords));
1222     if (drawHasse) {
1223       color = colors[depth%numColors];
1224       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1225       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1226       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1227       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1228       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1229 
1230       color = colors[1%numColors];
1231       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1232       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1233       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1234       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1235       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1236 
1237       color = colors[0%numColors];
1238       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1239       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1240       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1241       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1242       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1243 
1244       for (p = pStart; p < pEnd; ++p) {
1245         const PetscInt *cone;
1246         PetscInt        coneSize, cp;
1247 
1248         PetscCall(DMPlexGetCone(dm, p, &cone));
1249         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1250         for (cp = 0; cp < coneSize; ++cp) {
1251           PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1252         }
1253       }
1254     }
1255     PetscCall(PetscViewerFlush(viewer));
1256     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1257     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1258     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1259     for (l = 0; l < numLabels;  ++l) PetscCall(PetscFree(names[l]));
1260     for (c = 0; c < numColors;  ++c) PetscCall(PetscFree(colors[c]));
1261     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1262     PetscCall(PetscFree3(names, colors, lcolors));
1263     PetscCall(PetscBTDestroy(&wp));
1264   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1265     Vec                    cown,acown;
1266     VecScatter             sct;
1267     ISLocalToGlobalMapping g2l;
1268     IS                     gid,acis;
1269     MPI_Comm               comm,ncomm = MPI_COMM_NULL;
1270     MPI_Group              ggroup,ngroup;
1271     PetscScalar            *array,nid;
1272     const PetscInt         *idxs;
1273     PetscInt               *idxs2,*start,*adjacency,*work;
1274     PetscInt64             lm[3],gm[3];
1275     PetscInt               i,c,cStart,cEnd,cum,numVertices,ect,ectn,cellHeight;
1276     PetscMPIInt            d1,d2,rank;
1277 
1278     PetscCall(PetscObjectGetComm((PetscObject)dm,&comm));
1279     PetscCallMPI(MPI_Comm_rank(comm,&rank));
1280 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1281     PetscCallMPI(MPI_Comm_split_type(comm,MPI_COMM_TYPE_SHARED,rank,MPI_INFO_NULL,&ncomm));
1282 #endif
1283     if (ncomm != MPI_COMM_NULL) {
1284       PetscCallMPI(MPI_Comm_group(comm,&ggroup));
1285       PetscCallMPI(MPI_Comm_group(ncomm,&ngroup));
1286       d1   = 0;
1287       PetscCallMPI(MPI_Group_translate_ranks(ngroup,1,&d1,ggroup,&d2));
1288       nid  = d2;
1289       PetscCallMPI(MPI_Group_free(&ggroup));
1290       PetscCallMPI(MPI_Group_free(&ngroup));
1291       PetscCallMPI(MPI_Comm_free(&ncomm));
1292     } else nid = 0.0;
1293 
1294     /* Get connectivity */
1295     PetscCall(DMPlexGetVTKCellHeight(dm,&cellHeight));
1296     PetscCall(DMPlexCreatePartitionerGraph(dm,cellHeight,&numVertices,&start,&adjacency,&gid));
1297 
1298     /* filter overlapped local cells */
1299     PetscCall(DMPlexGetHeightStratum(dm,cellHeight,&cStart,&cEnd));
1300     PetscCall(ISGetIndices(gid,&idxs));
1301     PetscCall(ISGetLocalSize(gid,&cum));
1302     PetscCall(PetscMalloc1(cum,&idxs2));
1303     for (c = cStart, cum = 0; c < cEnd; c++) {
1304       if (idxs[c-cStart] < 0) continue;
1305       idxs2[cum++] = idxs[c-cStart];
1306     }
1307     PetscCall(ISRestoreIndices(gid,&idxs));
1308     PetscCheck(numVertices == cum,PETSC_COMM_SELF,PETSC_ERR_PLIB,"Unexpected %" PetscInt_FMT " != %" PetscInt_FMT,numVertices,cum);
1309     PetscCall(ISDestroy(&gid));
1310     PetscCall(ISCreateGeneral(comm,numVertices,idxs2,PETSC_OWN_POINTER,&gid));
1311 
1312     /* support for node-aware cell locality */
1313     PetscCall(ISCreateGeneral(comm,start[numVertices],adjacency,PETSC_USE_POINTER,&acis));
1314     PetscCall(VecCreateSeq(PETSC_COMM_SELF,start[numVertices],&acown));
1315     PetscCall(VecCreateMPI(comm,numVertices,PETSC_DECIDE,&cown));
1316     PetscCall(VecGetArray(cown,&array));
1317     for (c = 0; c < numVertices; c++) array[c] = nid;
1318     PetscCall(VecRestoreArray(cown,&array));
1319     PetscCall(VecScatterCreate(cown,acis,acown,NULL,&sct));
1320     PetscCall(VecScatterBegin(sct,cown,acown,INSERT_VALUES,SCATTER_FORWARD));
1321     PetscCall(VecScatterEnd(sct,cown,acown,INSERT_VALUES,SCATTER_FORWARD));
1322     PetscCall(ISDestroy(&acis));
1323     PetscCall(VecScatterDestroy(&sct));
1324     PetscCall(VecDestroy(&cown));
1325 
1326     /* compute edgeCut */
1327     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum,start[c+1]-start[c]);
1328     PetscCall(PetscMalloc1(cum,&work));
1329     PetscCall(ISLocalToGlobalMappingCreateIS(gid,&g2l));
1330     PetscCall(ISLocalToGlobalMappingSetType(g2l,ISLOCALTOGLOBALMAPPINGHASH));
1331     PetscCall(ISDestroy(&gid));
1332     PetscCall(VecGetArray(acown,&array));
1333     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1334       PetscInt totl;
1335 
1336       totl = start[c+1]-start[c];
1337       PetscCall(ISGlobalToLocalMappingApply(g2l,IS_GTOLM_MASK,totl,adjacency+start[c],NULL,work));
1338       for (i = 0; i < totl; i++) {
1339         if (work[i] < 0) {
1340           ect  += 1;
1341           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1342         }
1343       }
1344     }
1345     PetscCall(PetscFree(work));
1346     PetscCall(VecRestoreArray(acown,&array));
1347     lm[0] = numVertices > 0 ?  numVertices : PETSC_MAX_INT;
1348     lm[1] = -numVertices;
1349     PetscCall(MPIU_Allreduce(lm,gm,2,MPIU_INT64,MPI_MIN,comm));
1350     PetscCall(PetscViewerASCIIPrintf(viewer,"  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT,-((double)gm[1])/((double)gm[0]),-(PetscInt)gm[1],(PetscInt)gm[0]));
1351     lm[0] = ect; /* edgeCut */
1352     lm[1] = ectn; /* node-aware edgeCut */
1353     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1354     PetscCall(MPIU_Allreduce(lm,gm,3,MPIU_INT64,MPI_SUM,comm));
1355     PetscCall(PetscViewerASCIIPrintf(viewer,", empty %" PetscInt_FMT ")\n",(PetscInt)gm[2]));
1356 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1357     PetscCall(PetscViewerASCIIPrintf(viewer,"  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n",(PetscInt)(gm[0]/2),gm[0] ? ((double)(gm[1]))/((double)gm[0]) : 1.));
1358 #else
1359     PetscCall(PetscViewerASCIIPrintf(viewer,"  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n",(PetscInt)(gm[0]/2),0.0));
1360 #endif
1361     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1362     PetscCall(PetscFree(start));
1363     PetscCall(PetscFree(adjacency));
1364     PetscCall(VecDestroy(&acown));
1365   } else {
1366     const char    *name;
1367     PetscInt      *sizes, *hybsizes, *ghostsizes;
1368     PetscInt       locDepth, depth, cellHeight, dim, d;
1369     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1370     PetscInt       numLabels, l, maxSize = 17;
1371     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1372     MPI_Comm       comm;
1373     PetscMPIInt    size, rank;
1374 
1375     PetscCall(PetscObjectGetComm((PetscObject) dm, &comm));
1376     PetscCallMPI(MPI_Comm_size(comm, &size));
1377     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1378     PetscCall(DMGetDimension(dm, &dim));
1379     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1380     PetscCall(PetscObjectGetName((PetscObject) dm, &name));
1381     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1382     else      PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1383     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1384     PetscCall(DMPlexGetDepth(dm, &locDepth));
1385     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1386     PetscCall(DMPlexGetGhostCellStratum(dm, &gcStart, &gcEnd));
1387     gcNum = gcEnd - gcStart;
1388     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1389     else                PetscCall(PetscCalloc3(3,    &sizes, 3,    &hybsizes, 3,    &ghostsizes));
1390     for (d = 0; d <= depth; d++) {
1391       PetscInt Nc[2] = {0, 0}, ict;
1392 
1393       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1394       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1395       ict  = ct0;
1396       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1397       ct0  = (DMPolytopeType) ict;
1398       for (p = pStart; p < pEnd; ++p) {
1399         DMPolytopeType ct;
1400 
1401         PetscCall(DMPlexGetCellType(dm, p, &ct));
1402         if (ct == ct0) ++Nc[0];
1403         else           ++Nc[1];
1404       }
1405       if (size < maxSize) {
1406         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes,    1, MPIU_INT, 0, comm));
1407         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1408         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1409         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1410         for (p = 0; p < size; ++p) {
1411           if (rank == 0) {
1412             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p]+hybsizes[p]));
1413             if (hybsizes[p]   > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1414             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1415           }
1416         }
1417       } else {
1418         PetscInt locMinMax[2];
1419 
1420         locMinMax[0] = Nc[0]+Nc[1]; locMinMax[1] = Nc[0]+Nc[1];
1421         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1422         locMinMax[0] = Nc[1]; locMinMax[1] = Nc[1];
1423         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1424         if (d == depth) {
1425           locMinMax[0] = gcNum; locMinMax[1] = gcNum;
1426           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1427         }
1428         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1429         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1430         if (hybsizes[0]   > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1431         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1432       }
1433       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1434     }
1435     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1436     {
1437       const PetscReal *maxCell;
1438       const PetscReal *L;
1439       PetscBool        localized;
1440 
1441       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1442       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1443       if (L || localized) {
1444         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1445         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1446         if (L) {
1447           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1448           for (d = 0; d < dim; ++d) {
1449             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1450             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1451           }
1452           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1453         }
1454         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1455         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1456       }
1457     }
1458     PetscCall(DMGetNumLabels(dm, &numLabels));
1459     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1460     for (l = 0; l < numLabels; ++l) {
1461       DMLabel         label;
1462       const char     *name;
1463       IS              valueIS;
1464       const PetscInt *values;
1465       PetscInt        numValues, v;
1466 
1467       PetscCall(DMGetLabelName(dm, l, &name));
1468       PetscCall(DMGetLabel(dm, name, &label));
1469       PetscCall(DMLabelGetNumValues(label, &numValues));
1470       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1471       PetscCall(DMLabelGetValueIS(label, &valueIS));
1472       PetscCall(ISGetIndices(valueIS, &values));
1473       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1474       for (v = 0; v < numValues; ++v) {
1475         PetscInt size;
1476 
1477         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1478         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1479         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1480       }
1481       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1482       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1483       PetscCall(ISRestoreIndices(valueIS, &values));
1484       PetscCall(ISDestroy(&valueIS));
1485     }
1486     {
1487       char    **labelNames;
1488       PetscInt  Nl = numLabels;
1489       PetscBool flg;
1490 
1491       PetscCall(PetscMalloc1(Nl, &labelNames));
1492       PetscCall(PetscOptionsGetStringArray(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1493       for (l = 0; l < Nl; ++l) {
1494         DMLabel label;
1495 
1496         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1497         if (flg) {
1498           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1499           PetscCall(DMLabelView(label, viewer));
1500         }
1501         PetscCall(PetscFree(labelNames[l]));
1502       }
1503       PetscCall(PetscFree(labelNames));
1504     }
1505     /* If no fields are specified, people do not want to see adjacency */
1506     if (dm->Nf) {
1507       PetscInt f;
1508 
1509       for (f = 0; f < dm->Nf; ++f) {
1510         const char *name;
1511 
1512         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1513         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1514         PetscCall(PetscViewerASCIIPushTab(viewer));
1515         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1516         if (dm->fields[f].adjacency[0]) {
1517           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1518           else                            PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1519         } else {
1520           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1521           else                            PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1522         }
1523         PetscCall(PetscViewerASCIIPopTab(viewer));
1524       }
1525     }
1526     PetscCall(DMGetCoarseDM(dm, &cdm));
1527     if (cdm) {
1528       PetscCall(PetscViewerASCIIPushTab(viewer));
1529       PetscCall(DMPlexView_Ascii(cdm, viewer));
1530       PetscCall(PetscViewerASCIIPopTab(viewer));
1531     }
1532   }
1533   PetscFunctionReturn(0);
1534 }
1535 
1536 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1537 {
1538   DMPolytopeType ct;
1539   PetscMPIInt    rank;
1540   PetscInt       cdim;
1541 
1542   PetscFunctionBegin;
1543   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject) dm), &rank));
1544   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1545   PetscCall(DMGetCoordinateDim(dm, &cdim));
1546   switch (ct) {
1547   case DM_POLYTOPE_SEGMENT:
1548   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1549     switch (cdim) {
1550     case 1:
1551     {
1552       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1553       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1554 
1555       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y,    PetscRealPart(coords[1]), y,    PETSC_DRAW_BLACK));
1556       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y+dy, PetscRealPart(coords[0]), y-dy, PETSC_DRAW_BLACK));
1557       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y+dy, PetscRealPart(coords[1]), y-dy, PETSC_DRAW_BLACK));
1558     }
1559     break;
1560     case 2:
1561     {
1562       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1563       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1564       const PetscReal l  = 0.1/PetscSqrtReal(dx*dx + dy*dy);
1565 
1566       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1567       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));
1568       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));
1569     }
1570     break;
1571     default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1572     }
1573     break;
1574   case DM_POLYTOPE_TRIANGLE:
1575     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]),
1576                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1577                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1578                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2));
1579     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1580     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1581     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1582     break;
1583   case DM_POLYTOPE_QUADRILATERAL:
1584     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]),
1585                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1586                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1587                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2));
1588     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]),
1589                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1590                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1591                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2));
1592     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1593     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1594     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1595     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1596     break;
1597   default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1598   }
1599   PetscFunctionReturn(0);
1600 }
1601 
1602 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1603 {
1604   DMPolytopeType ct;
1605   PetscReal      centroid[2] = {0., 0.};
1606   PetscMPIInt    rank;
1607   PetscInt       fillColor, v, e, d;
1608 
1609   PetscFunctionBegin;
1610   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject) dm), &rank));
1611   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1612   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2;
1613   switch (ct) {
1614   case DM_POLYTOPE_TRIANGLE:
1615     {
1616       PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1617 
1618       for (v = 0; v < 3; ++v) {centroid[0] += PetscRealPart(coords[v*2+0])/3.;centroid[1] += PetscRealPart(coords[v*2+1])/3.;}
1619       for (e = 0; e < 3; ++e) {
1620         refCoords[0] = refVertices[e*2+0];
1621         refCoords[1] = refVertices[e*2+1];
1622         for (d = 1; d <= edgeDiv; ++d) {
1623           refCoords[d*2+0] = refCoords[0] + (refVertices[(e+1)%3 * 2 + 0] - refCoords[0])*d/edgeDiv;
1624           refCoords[d*2+1] = refCoords[1] + (refVertices[(e+1)%3 * 2 + 1] - refCoords[1])*d/edgeDiv;
1625         }
1626         PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv+1, refCoords, edgeCoords));
1627         for (d = 0; d < edgeDiv; ++d) {
1628           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));
1629           PetscCall(PetscDrawLine(draw, edgeCoords[d*2+0], edgeCoords[d*2+1], edgeCoords[(d+1)*2+0], edgeCoords[(d+1)*2+1], PETSC_DRAW_BLACK));
1630         }
1631       }
1632     }
1633     break;
1634   default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1635   }
1636   PetscFunctionReturn(0);
1637 }
1638 
1639 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1640 {
1641   PetscDraw          draw;
1642   DM                 cdm;
1643   PetscSection       coordSection;
1644   Vec                coordinates;
1645   const PetscScalar *coords;
1646   PetscReal          xyl[2],xyr[2],bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
1647   PetscReal         *refCoords, *edgeCoords;
1648   PetscBool          isnull, drawAffine = PETSC_TRUE;
1649   PetscInt           dim, vStart, vEnd, cStart, cEnd, c, N, edgeDiv = 4;
1650 
1651   PetscFunctionBegin;
1652   PetscCall(DMGetCoordinateDim(dm, &dim));
1653   PetscCheck(dim <= 2,PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1654   PetscCall(PetscOptionsGetBool(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1655   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv+1)*dim, &refCoords, (edgeDiv+1)*dim, &edgeCoords));
1656   PetscCall(DMGetCoordinateDM(dm, &cdm));
1657   PetscCall(DMGetLocalSection(cdm, &coordSection));
1658   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1659   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1660   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1661 
1662   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1663   PetscCall(PetscDrawIsNull(draw, &isnull));
1664   if (isnull) PetscFunctionReturn(0);
1665   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1666 
1667   PetscCall(VecGetLocalSize(coordinates, &N));
1668   PetscCall(VecGetArrayRead(coordinates, &coords));
1669   for (c = 0; c < N; c += dim) {
1670     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));   bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
1671     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c+1])); bound[3] = PetscMax(bound[3], PetscRealPart(coords[c+1]));
1672   }
1673   PetscCall(VecRestoreArrayRead(coordinates, &coords));
1674   PetscCall(MPIU_Allreduce(&bound[0],xyl,2,MPIU_REAL,MPIU_MIN,PetscObjectComm((PetscObject)dm)));
1675   PetscCall(MPIU_Allreduce(&bound[2],xyr,2,MPIU_REAL,MPIU_MAX,PetscObjectComm((PetscObject)dm)));
1676   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1677   PetscCall(PetscDrawClear(draw));
1678 
1679   for (c = cStart; c < cEnd; ++c) {
1680     PetscScalar *coords = NULL;
1681     PetscInt     numCoords;
1682 
1683     PetscCall(DMPlexVecGetClosureAtDepth_Internal(dm, coordSection, coordinates, c, 0, &numCoords, &coords));
1684     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1685     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1686     PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
1687   }
1688   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1689   PetscCall(PetscDrawFlush(draw));
1690   PetscCall(PetscDrawPause(draw));
1691   PetscCall(PetscDrawSave(draw));
1692   PetscFunctionReturn(0);
1693 }
1694 
1695 #if defined(PETSC_HAVE_EXODUSII)
1696 #include <exodusII.h>
1697 #include <petscviewerexodusii.h>
1698 #endif
1699 
1700 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1701 {
1702   PetscBool      iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus;
1703   char           name[PETSC_MAX_PATH_LEN];
1704 
1705   PetscFunctionBegin;
1706   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1707   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1708   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERASCII,    &iascii));
1709   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,      &isvtk));
1710   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,     &ishdf5));
1711   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERDRAW,     &isdraw));
1712   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERGLVIS,    &isglvis));
1713   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWEREXODUSII, &isexodus));
1714   if (iascii) {
1715     PetscViewerFormat format;
1716     PetscCall(PetscViewerGetFormat(viewer, &format));
1717     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1718     else PetscCall(DMPlexView_Ascii(dm, viewer));
1719   } else if (ishdf5) {
1720 #if defined(PETSC_HAVE_HDF5)
1721     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1722 #else
1723     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1724 #endif
1725   } else if (isvtk) {
1726     PetscCall(DMPlexVTKWriteAll((PetscObject) dm,viewer));
1727   } else if (isdraw) {
1728     PetscCall(DMPlexView_Draw(dm, viewer));
1729   } else if (isglvis) {
1730     PetscCall(DMPlexView_GLVis(dm, viewer));
1731 #if defined(PETSC_HAVE_EXODUSII)
1732   } else if (isexodus) {
1733 /*
1734       exodusII requires that all sets be part of exactly one cell set.
1735       If the dm does not have a "Cell Sets" label defined, we create one
1736       with ID 1, containig all cells.
1737       Note that if the Cell Sets label is defined but does not cover all cells,
1738       we may still have a problem. This should probably be checked here or in the viewer;
1739     */
1740     PetscInt numCS;
1741     PetscCall(DMGetLabelSize(dm,"Cell Sets",&numCS));
1742     if (!numCS) {
1743       PetscInt cStart, cEnd, c;
1744       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1745       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1746       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1747     }
1748     PetscCall(DMView_PlexExodusII(dm, viewer));
1749 #endif
1750   } else SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1751 
1752   /* Optionally view the partition */
1753   PetscCall(PetscOptionsHasName(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_partition_view", &flg));
1754   if (flg) {
1755     Vec ranks;
1756     PetscCall(DMPlexCreateRankField(dm, &ranks));
1757     PetscCall(VecView(ranks, viewer));
1758     PetscCall(VecDestroy(&ranks));
1759   }
1760   /* Optionally view a label */
1761   PetscCall(PetscOptionsGetString(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1762   if (flg) {
1763     DMLabel label;
1764     Vec     val;
1765 
1766     PetscCall(DMGetLabel(dm, name, &label));
1767     PetscCheck(label,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1768     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1769     PetscCall(VecView(val, viewer));
1770     PetscCall(VecDestroy(&val));
1771   }
1772   PetscFunctionReturn(0);
1773 }
1774 
1775 /*@
1776   DMPlexTopologyView - Saves a DMPlex topology into a file
1777 
1778   Collective on DM
1779 
1780   Input Parameters:
1781 + dm     - The DM whose topology is to be saved
1782 - viewer - The PetscViewer for saving
1783 
1784   Level: advanced
1785 
1786 .seealso: `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`
1787 @*/
1788 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
1789 {
1790   PetscBool      ishdf5;
1791 
1792   PetscFunctionBegin;
1793   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1794   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1795   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
1796   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView,viewer,0,0,0));
1797   if (ishdf5) {
1798 #if defined(PETSC_HAVE_HDF5)
1799     PetscViewerFormat format;
1800     PetscCall(PetscViewerGetFormat(viewer, &format));
1801     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1802       IS globalPointNumbering;
1803 
1804       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1805       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
1806       PetscCall(ISDestroy(&globalPointNumbering));
1807     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1808 #else
1809     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1810 #endif
1811   }
1812   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView,viewer,0,0,0));
1813   PetscFunctionReturn(0);
1814 }
1815 
1816 /*@
1817   DMPlexCoordinatesView - Saves DMPlex coordinates into a file
1818 
1819   Collective on DM
1820 
1821   Input Parameters:
1822 + dm     - The DM whose coordinates are to be saved
1823 - viewer - The PetscViewer for saving
1824 
1825   Level: advanced
1826 
1827 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`
1828 @*/
1829 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
1830 {
1831   PetscBool      ishdf5;
1832 
1833   PetscFunctionBegin;
1834   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1835   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1836   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
1837   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView,viewer,0,0,0));
1838   if (ishdf5) {
1839 #if defined(PETSC_HAVE_HDF5)
1840     PetscViewerFormat format;
1841     PetscCall(PetscViewerGetFormat(viewer, &format));
1842     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1843       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
1844     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1845 #else
1846     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1847 #endif
1848   }
1849   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView,viewer,0,0,0));
1850   PetscFunctionReturn(0);
1851 }
1852 
1853 /*@
1854   DMPlexLabelsView - Saves DMPlex labels into a file
1855 
1856   Collective on DM
1857 
1858   Input Parameters:
1859 + dm     - The DM whose labels are to be saved
1860 - viewer - The PetscViewer for saving
1861 
1862   Level: advanced
1863 
1864 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`
1865 @*/
1866 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
1867 {
1868   PetscBool      ishdf5;
1869 
1870   PetscFunctionBegin;
1871   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1872   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1873   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
1874   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView,viewer,0,0,0));
1875   if (ishdf5) {
1876 #if defined(PETSC_HAVE_HDF5)
1877     IS                globalPointNumbering;
1878     PetscViewerFormat format;
1879 
1880     PetscCall(PetscViewerGetFormat(viewer, &format));
1881     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1882       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1883       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
1884       PetscCall(ISDestroy(&globalPointNumbering));
1885     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1886 #else
1887     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1888 #endif
1889   }
1890   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView,viewer,0,0,0));
1891   PetscFunctionReturn(0);
1892 }
1893 
1894 /*@
1895   DMPlexSectionView - Saves a section associated with a DMPlex
1896 
1897   Collective on DM
1898 
1899   Input Parameters:
1900 + dm         - The DM that contains the topology on which the section to be saved is defined
1901 . viewer     - The PetscViewer for saving
1902 - sectiondm  - The DM that contains the section to be saved
1903 
1904   Level: advanced
1905 
1906   Notes:
1907   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.
1908 
1909   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.
1910 
1911 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`
1912 @*/
1913 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
1914 {
1915   PetscBool      ishdf5;
1916 
1917   PetscFunctionBegin;
1918   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1919   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1920   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1921   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
1922   PetscCall(PetscLogEventBegin(DMPLEX_SectionView,viewer,0,0,0));
1923   if (ishdf5) {
1924 #if defined(PETSC_HAVE_HDF5)
1925     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
1926 #else
1927     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1928 #endif
1929   }
1930   PetscCall(PetscLogEventEnd(DMPLEX_SectionView,viewer,0,0,0));
1931   PetscFunctionReturn(0);
1932 }
1933 
1934 /*@
1935   DMPlexGlobalVectorView - Saves a global vector
1936 
1937   Collective on DM
1938 
1939   Input Parameters:
1940 + dm        - The DM that represents the topology
1941 . viewer    - The PetscViewer to save data with
1942 . sectiondm - The DM that contains the global section on which vec is defined
1943 - vec       - The global vector to be saved
1944 
1945   Level: advanced
1946 
1947   Notes:
1948   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.
1949 
1950   Typical calling sequence
1951 $       DMCreate(PETSC_COMM_WORLD, &dm);
1952 $       DMSetType(dm, DMPLEX);
1953 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
1954 $       DMClone(dm, &sectiondm);
1955 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
1956 $       PetscSectionCreate(PETSC_COMM_WORLD, &section);
1957 $       DMPlexGetChart(sectiondm, &pStart, &pEnd);
1958 $       PetscSectionSetChart(section, pStart, pEnd);
1959 $       PetscSectionSetUp(section);
1960 $       DMSetLocalSection(sectiondm, section);
1961 $       PetscSectionDestroy(&section);
1962 $       DMGetGlobalVector(sectiondm, &vec);
1963 $       PetscObjectSetName((PetscObject)vec, "vec_name");
1964 $       DMPlexTopologyView(dm, viewer);
1965 $       DMPlexSectionView(dm, viewer, sectiondm);
1966 $       DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
1967 $       DMRestoreGlobalVector(sectiondm, &vec);
1968 $       DMDestroy(&sectiondm);
1969 $       DMDestroy(&dm);
1970 
1971 .seealso: `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
1972 @*/
1973 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
1974 {
1975   PetscBool       ishdf5;
1976 
1977   PetscFunctionBegin;
1978   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1979   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1980   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1981   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
1982   /* Check consistency */
1983   {
1984     PetscSection  section;
1985     PetscBool     includesConstraints;
1986     PetscInt      m, m1;
1987 
1988     PetscCall(VecGetLocalSize(vec, &m1));
1989     PetscCall(DMGetGlobalSection(sectiondm, &section));
1990     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
1991     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
1992     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
1993     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
1994   }
1995   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1996   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView,viewer,0,0,0));
1997   if (ishdf5) {
1998 #if defined(PETSC_HAVE_HDF5)
1999     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2000 #else
2001     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2002 #endif
2003   }
2004   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView,viewer,0,0,0));
2005   PetscFunctionReturn(0);
2006 }
2007 
2008 /*@
2009   DMPlexLocalVectorView - Saves a local vector
2010 
2011   Collective on DM
2012 
2013   Input Parameters:
2014 + dm        - The DM that represents the topology
2015 . viewer    - The PetscViewer to save data with
2016 . sectiondm - The DM that contains the local section on which vec is defined; may be the same as dm
2017 - vec       - The local vector to be saved
2018 
2019   Level: advanced
2020 
2021   Notes:
2022   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.
2023 
2024   Typical calling sequence
2025 $       DMCreate(PETSC_COMM_WORLD, &dm);
2026 $       DMSetType(dm, DMPLEX);
2027 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2028 $       DMClone(dm, &sectiondm);
2029 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2030 $       PetscSectionCreate(PETSC_COMM_WORLD, &section);
2031 $       DMPlexGetChart(sectiondm, &pStart, &pEnd);
2032 $       PetscSectionSetChart(section, pStart, pEnd);
2033 $       PetscSectionSetUp(section);
2034 $       DMSetLocalSection(sectiondm, section);
2035 $       DMGetLocalVector(sectiondm, &vec);
2036 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2037 $       DMPlexTopologyView(dm, viewer);
2038 $       DMPlexSectionView(dm, viewer, sectiondm);
2039 $       DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2040 $       DMRestoreLocalVector(sectiondm, &vec);
2041 $       DMDestroy(&sectiondm);
2042 $       DMDestroy(&dm);
2043 
2044 .seealso: `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2045 @*/
2046 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2047 {
2048   PetscBool       ishdf5;
2049 
2050   PetscFunctionBegin;
2051   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2052   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2053   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2054   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2055   /* Check consistency */
2056   {
2057     PetscSection  section;
2058     PetscBool     includesConstraints;
2059     PetscInt      m, m1;
2060 
2061     PetscCall(VecGetLocalSize(vec, &m1));
2062     PetscCall(DMGetLocalSection(sectiondm, &section));
2063     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2064     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2065     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2066     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2067   }
2068   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2069   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView,viewer,0,0,0));
2070   if (ishdf5) {
2071 #if defined(PETSC_HAVE_HDF5)
2072     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2073 #else
2074     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2075 #endif
2076   }
2077   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView,viewer,0,0,0));
2078   PetscFunctionReturn(0);
2079 }
2080 
2081 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2082 {
2083   PetscBool      ishdf5;
2084 
2085   PetscFunctionBegin;
2086   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2087   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2088   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,   &ishdf5));
2089   if (ishdf5) {
2090 #if defined(PETSC_HAVE_HDF5)
2091     PetscViewerFormat format;
2092     PetscCall(PetscViewerGetFormat(viewer, &format));
2093     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2094       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2095     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2096       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2097     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2098     PetscFunctionReturn(0);
2099 #else
2100     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2101 #endif
2102   } else SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2103 }
2104 
2105 /*@
2106   DMPlexTopologyLoad - Loads a topology into a DMPlex
2107 
2108   Collective on DM
2109 
2110   Input Parameters:
2111 + dm     - The DM into which the topology is loaded
2112 - viewer - The PetscViewer for the saved topology
2113 
2114   Output Parameters:
2115 . 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
2116 
2117   Level: advanced
2118 
2119 .seealso: `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2120 @*/
2121 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2122 {
2123   PetscBool      ishdf5;
2124 
2125   PetscFunctionBegin;
2126   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2127   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2128   if (globalToLocalPointSF) PetscValidPointer(globalToLocalPointSF, 3);
2129   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
2130   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad,viewer,0,0,0));
2131   if (ishdf5) {
2132 #if defined(PETSC_HAVE_HDF5)
2133     PetscViewerFormat format;
2134     PetscCall(PetscViewerGetFormat(viewer, &format));
2135     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2136       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2137     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2138 #else
2139     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2140 #endif
2141   }
2142   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad,viewer,0,0,0));
2143   PetscFunctionReturn(0);
2144 }
2145 
2146 /*@
2147   DMPlexCoordinatesLoad - Loads coordinates into a DMPlex
2148 
2149   Collective on DM
2150 
2151   Input Parameters:
2152 + dm     - The DM into which the coordinates are loaded
2153 . viewer - The PetscViewer for the saved coordinates
2154 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2155 
2156   Level: advanced
2157 
2158 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2159 @*/
2160 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2161 {
2162   PetscBool      ishdf5;
2163 
2164   PetscFunctionBegin;
2165   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2166   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2167   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2168   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
2169   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad,viewer,0,0,0));
2170   if (ishdf5) {
2171 #if defined(PETSC_HAVE_HDF5)
2172     PetscViewerFormat format;
2173     PetscCall(PetscViewerGetFormat(viewer, &format));
2174     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2175       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2176     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2177 #else
2178     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2179 #endif
2180   }
2181   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad,viewer,0,0,0));
2182   PetscFunctionReturn(0);
2183 }
2184 
2185 /*@
2186   DMPlexLabelsLoad - Loads labels into a DMPlex
2187 
2188   Collective on DM
2189 
2190   Input Parameters:
2191 + dm     - The DM into which the labels are loaded
2192 . viewer - The PetscViewer for the saved labels
2193 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2194 
2195   Level: advanced
2196 
2197   Notes:
2198   The PetscSF argument must not be NULL if the DM is distributed, otherwise an error occurs.
2199 
2200 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2201 @*/
2202 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2203 {
2204   PetscBool      ishdf5;
2205 
2206   PetscFunctionBegin;
2207   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2208   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2209   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2210   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
2211   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad,viewer,0,0,0));
2212   if (ishdf5) {
2213 #if defined(PETSC_HAVE_HDF5)
2214     PetscViewerFormat format;
2215 
2216     PetscCall(PetscViewerGetFormat(viewer, &format));
2217     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2218       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2219     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2220 #else
2221     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2222 #endif
2223   }
2224   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad,viewer,0,0,0));
2225   PetscFunctionReturn(0);
2226 }
2227 
2228 /*@
2229   DMPlexSectionLoad - Loads section into a DMPlex
2230 
2231   Collective on DM
2232 
2233   Input Parameters:
2234 + dm          - The DM that represents the topology
2235 . viewer      - The PetscViewer that represents the on-disk section (sectionA)
2236 . sectiondm   - The DM into which the on-disk section (sectionA) is migrated
2237 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2238 
2239   Output Parameters
2240 + 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)
2241 - 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)
2242 
2243   Level: advanced
2244 
2245   Notes:
2246   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.
2247 
2248   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.
2249 
2250   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.
2251 
2252   Example using 2 processes:
2253 $  NX (number of points on dm): 4
2254 $  sectionA                   : the on-disk section
2255 $  vecA                       : a vector associated with sectionA
2256 $  sectionB                   : sectiondm's local section constructed in this function
2257 $  vecB (local)               : a vector associated with sectiondm's local section
2258 $  vecB (global)              : a vector associated with sectiondm's global section
2259 $
2260 $                                     rank 0    rank 1
2261 $  vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2262 $  sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2263 $  sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2264 $  sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2265 $  [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2266 $  sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2267 $  sectionB->atlasDof             :     1 0 1 | 1 3
2268 $  sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2269 $  vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2270 $  vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2271 $
2272 $  where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2273 
2274 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`
2275 @*/
2276 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2277 {
2278   PetscBool      ishdf5;
2279 
2280   PetscFunctionBegin;
2281   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2282   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2283   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2284   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2285   if (globalDofSF) PetscValidPointer(globalDofSF, 5);
2286   if (localDofSF) PetscValidPointer(localDofSF, 6);
2287   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
2288   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad,viewer,0,0,0));
2289   if (ishdf5) {
2290 #if defined(PETSC_HAVE_HDF5)
2291     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2292 #else
2293     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2294 #endif
2295   }
2296   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad,viewer,0,0,0));
2297   PetscFunctionReturn(0);
2298 }
2299 
2300 /*@
2301   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2302 
2303   Collective on DM
2304 
2305   Input Parameters:
2306 + dm        - The DM that represents the topology
2307 . viewer    - The PetscViewer that represents the on-disk vector data
2308 . sectiondm - The DM that contains the global section on which vec is defined
2309 . sf        - The SF that migrates the on-disk vector data into vec
2310 - vec       - The global vector to set values of
2311 
2312   Level: advanced
2313 
2314   Notes:
2315   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.
2316 
2317   Typical calling sequence
2318 $       DMCreate(PETSC_COMM_WORLD, &dm);
2319 $       DMSetType(dm, DMPLEX);
2320 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2321 $       DMPlexTopologyLoad(dm, viewer, &sfX);
2322 $       DMClone(dm, &sectiondm);
2323 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2324 $       DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2325 $       DMGetGlobalVector(sectiondm, &vec);
2326 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2327 $       DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2328 $       DMRestoreGlobalVector(sectiondm, &vec);
2329 $       PetscSFDestroy(&gsf);
2330 $       PetscSFDestroy(&sfX);
2331 $       DMDestroy(&sectiondm);
2332 $       DMDestroy(&dm);
2333 
2334 .seealso: `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`
2335 @*/
2336 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2337 {
2338   PetscBool       ishdf5;
2339 
2340   PetscFunctionBegin;
2341   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2342   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2343   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2344   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2345   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2346   /* Check consistency */
2347   {
2348     PetscSection  section;
2349     PetscBool     includesConstraints;
2350     PetscInt      m, m1;
2351 
2352     PetscCall(VecGetLocalSize(vec, &m1));
2353     PetscCall(DMGetGlobalSection(sectiondm, &section));
2354     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2355     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2356     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2357     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2358   }
2359   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
2360   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad,viewer,0,0,0));
2361   if (ishdf5) {
2362 #if defined(PETSC_HAVE_HDF5)
2363     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2364 #else
2365     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2366 #endif
2367   }
2368   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad,viewer,0,0,0));
2369   PetscFunctionReturn(0);
2370 }
2371 
2372 /*@
2373   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2374 
2375   Collective on DM
2376 
2377   Input Parameters:
2378 + dm        - The DM that represents the topology
2379 . viewer    - The PetscViewer that represents the on-disk vector data
2380 . sectiondm - The DM that contains the local section on which vec is defined
2381 . sf        - The SF that migrates the on-disk vector data into vec
2382 - vec       - The local vector to set values of
2383 
2384   Level: advanced
2385 
2386   Notes:
2387   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2388 
2389   Typical calling sequence
2390 $       DMCreate(PETSC_COMM_WORLD, &dm);
2391 $       DMSetType(dm, DMPLEX);
2392 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2393 $       DMPlexTopologyLoad(dm, viewer, &sfX);
2394 $       DMClone(dm, &sectiondm);
2395 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2396 $       DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2397 $       DMGetLocalVector(sectiondm, &vec);
2398 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2399 $       DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2400 $       DMRestoreLocalVector(sectiondm, &vec);
2401 $       PetscSFDestroy(&lsf);
2402 $       PetscSFDestroy(&sfX);
2403 $       DMDestroy(&sectiondm);
2404 $       DMDestroy(&dm);
2405 
2406 .seealso: `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`
2407 @*/
2408 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2409 {
2410   PetscBool       ishdf5;
2411 
2412   PetscFunctionBegin;
2413   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2414   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2415   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2416   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2417   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2418   /* Check consistency */
2419   {
2420     PetscSection  section;
2421     PetscBool     includesConstraints;
2422     PetscInt      m, m1;
2423 
2424     PetscCall(VecGetLocalSize(vec, &m1));
2425     PetscCall(DMGetLocalSection(sectiondm, &section));
2426     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2427     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2428     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2429     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2430   }
2431   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
2432   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad,viewer,0,0,0));
2433   if (ishdf5) {
2434 #if defined(PETSC_HAVE_HDF5)
2435     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2436 #else
2437     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2438 #endif
2439   }
2440   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad,viewer,0,0,0));
2441   PetscFunctionReturn(0);
2442 }
2443 
2444 PetscErrorCode DMDestroy_Plex(DM dm)
2445 {
2446   DM_Plex       *mesh = (DM_Plex*) dm->data;
2447 
2448   PetscFunctionBegin;
2449   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMSetUpGLVisViewer_C",NULL));
2450   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexInsertBoundaryValues_C", NULL));
2451   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMCreateNeumannOverlap_C", NULL));
2452   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMInterpolateSolution_C", NULL));
2453   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexInsertTimeDerviativeBoundaryValues_C", NULL));
2454   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexGetOverlap_C", NULL));
2455   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexDistributeGetDefault_C", NULL));
2456   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexDistributeSetDefault_C", NULL));
2457   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"MatComputeNeumannOverlap_C",NULL));
2458   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexReorderGetDefault_C", NULL));
2459   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexReorderSetDefault_C", NULL));
2460   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexGetOverlap_C",NULL));
2461   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexSetOverlap_C",NULL));
2462   if (--mesh->refct > 0) PetscFunctionReturn(0);
2463   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2464   PetscCall(PetscFree(mesh->cones));
2465   PetscCall(PetscFree(mesh->coneOrientations));
2466   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2467   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2468   PetscCall(PetscFree(mesh->supports));
2469   PetscCall(PetscFree(mesh->facesTmp));
2470   PetscCall(PetscFree(mesh->tetgenOpts));
2471   PetscCall(PetscFree(mesh->triangleOpts));
2472   PetscCall(PetscFree(mesh->transformType));
2473   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2474   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2475   PetscCall(ISDestroy(&mesh->subpointIS));
2476   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2477   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2478   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2479   PetscCall(ISDestroy(&mesh->anchorIS));
2480   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2481   PetscCall(PetscFree(mesh->parents));
2482   PetscCall(PetscFree(mesh->childIDs));
2483   PetscCall(PetscSectionDestroy(&mesh->childSection));
2484   PetscCall(PetscFree(mesh->children));
2485   PetscCall(DMDestroy(&mesh->referenceTree));
2486   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2487   PetscCall(PetscFree(mesh->neighbors));
2488   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2489   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2490   PetscCall(PetscFree(mesh));
2491   PetscFunctionReturn(0);
2492 }
2493 
2494 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2495 {
2496   PetscSection           sectionGlobal;
2497   PetscInt               bs = -1, mbs;
2498   PetscInt               localSize, localStart = 0;
2499   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2500   MatType                mtype;
2501   ISLocalToGlobalMapping ltog;
2502 
2503   PetscFunctionBegin;
2504   PetscCall(MatInitializePackage());
2505   mtype = dm->mattype;
2506   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2507   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2508   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2509   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject) dm)));
2510   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2511   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2512   PetscCall(MatSetType(*J, mtype));
2513   PetscCall(MatSetFromOptions(*J));
2514   PetscCall(MatGetBlockSize(*J, &mbs));
2515   if (mbs > 1) bs = mbs;
2516   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2517   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2518   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2519   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2520   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2521   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2522   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2523   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2524   if (!isShell) {
2525     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2526     PetscInt  *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2527     PetscInt  pStart, pEnd, p, dof, cdof;
2528 
2529     PetscCall(DMGetLocalToGlobalMapping(dm,&ltog));
2530 
2531     PetscCall(PetscCalloc1(localSize, &pblocks));
2532     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2533     for (p = pStart; p < pEnd; ++p) {
2534       PetscInt bdof, offset;
2535 
2536       PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2537       PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2538       PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2539       for (PetscInt i=0; i < dof - cdof; i++)
2540         pblocks[offset - localStart + i] = dof - cdof;
2541       dof  = dof < 0 ? -(dof+1) : dof;
2542       bdof = cdof && (dof-cdof) ? 1 : dof;
2543       if (dof) {
2544         if (bs < 0)          {bs = bdof;}
2545         else if (bs != bdof) {bs = 1;}
2546       }
2547     }
2548     /* Must have same blocksize on all procs (some might have no points) */
2549     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2550     bsLocal[1] = bs;
2551     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject) dm), bsLocal, bsMinMax));
2552     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2553     else bs = bsMinMax[0];
2554     bs = PetscMax(1,bs);
2555     PetscCall(MatSetLocalToGlobalMapping(*J,ltog,ltog));
2556     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2557       PetscCall(MatSetBlockSize(*J, bs));
2558       PetscCall(MatSetUp(*J));
2559     } else {
2560       PetscCall(PetscCalloc4(localSize/bs, &dnz, localSize/bs, &onz, localSize/bs, &dnzu, localSize/bs, &onzu));
2561       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2562       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2563     }
2564     { // Consolidate blocks
2565       PetscInt nblocks = 0;
2566       for (PetscInt i=0; i<localSize; i += PetscMax(1, pblocks[i])) {
2567         if (pblocks[i] == 0) continue;
2568         pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2569         for (PetscInt j=1; j<pblocks[i]; j++) {
2570            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]);
2571         }
2572       }
2573       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2574     }
2575     PetscCall(PetscFree(pblocks));
2576   }
2577   PetscCall(MatSetDM(*J, dm));
2578   PetscFunctionReturn(0);
2579 }
2580 
2581 /*@
2582   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2583 
2584   Not collective
2585 
2586   Input Parameter:
2587 . mesh - The DMPlex
2588 
2589   Output Parameters:
2590 . subsection - The subdomain section
2591 
2592   Level: developer
2593 
2594 .seealso:
2595 @*/
2596 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2597 {
2598   DM_Plex       *mesh = (DM_Plex*) dm->data;
2599 
2600   PetscFunctionBegin;
2601   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2602   if (!mesh->subdomainSection) {
2603     PetscSection section;
2604     PetscSF      sf;
2605 
2606     PetscCall(PetscSFCreate(PETSC_COMM_SELF,&sf));
2607     PetscCall(DMGetLocalSection(dm,&section));
2608     PetscCall(PetscSectionCreateGlobalSection(section,sf,PETSC_FALSE,PETSC_TRUE,&mesh->subdomainSection));
2609     PetscCall(PetscSFDestroy(&sf));
2610   }
2611   *subsection = mesh->subdomainSection;
2612   PetscFunctionReturn(0);
2613 }
2614 
2615 /*@
2616   DMPlexGetChart - Return the interval for all mesh points [pStart, pEnd)
2617 
2618   Not collective
2619 
2620   Input Parameter:
2621 . mesh - The DMPlex
2622 
2623   Output Parameters:
2624 + pStart - The first mesh point
2625 - pEnd   - The upper bound for mesh points
2626 
2627   Level: beginner
2628 
2629 .seealso: `DMPlexCreate()`, `DMPlexSetChart()`
2630 @*/
2631 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2632 {
2633   DM_Plex       *mesh = (DM_Plex*) dm->data;
2634 
2635   PetscFunctionBegin;
2636   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2637   PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2638   PetscFunctionReturn(0);
2639 }
2640 
2641 /*@
2642   DMPlexSetChart - Set the interval for all mesh points [pStart, pEnd)
2643 
2644   Not collective
2645 
2646   Input Parameters:
2647 + mesh - The DMPlex
2648 . pStart - The first mesh point
2649 - pEnd   - The upper bound for mesh points
2650 
2651   Output Parameters:
2652 
2653   Level: beginner
2654 
2655 .seealso: `DMPlexCreate()`, `DMPlexGetChart()`
2656 @*/
2657 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2658 {
2659   DM_Plex       *mesh = (DM_Plex*) dm->data;
2660 
2661   PetscFunctionBegin;
2662   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2663   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2664   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2665   PetscFunctionReturn(0);
2666 }
2667 
2668 /*@
2669   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2670 
2671   Not collective
2672 
2673   Input Parameters:
2674 + mesh - The DMPlex
2675 - p - The point, which must lie in the chart set with DMPlexSetChart()
2676 
2677   Output Parameter:
2678 . size - The cone size for point p
2679 
2680   Level: beginner
2681 
2682 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2683 @*/
2684 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2685 {
2686   DM_Plex       *mesh = (DM_Plex*) dm->data;
2687 
2688   PetscFunctionBegin;
2689   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2690   PetscValidIntPointer(size, 3);
2691   PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2692   PetscFunctionReturn(0);
2693 }
2694 
2695 /*@
2696   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2697 
2698   Not collective
2699 
2700   Input Parameters:
2701 + mesh - The DMPlex
2702 . p - The point, which must lie in the chart set with DMPlexSetChart()
2703 - size - The cone size for point p
2704 
2705   Output Parameter:
2706 
2707   Note:
2708   This should be called after DMPlexSetChart().
2709 
2710   Level: beginner
2711 
2712 .seealso: `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2713 @*/
2714 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
2715 {
2716   DM_Plex       *mesh = (DM_Plex*) dm->data;
2717 
2718   PetscFunctionBegin;
2719   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2720   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
2721   PetscFunctionReturn(0);
2722 }
2723 
2724 /*@
2725   DMPlexAddConeSize - Add the given number of in-edges to this point in the DAG
2726 
2727   Not collective
2728 
2729   Input Parameters:
2730 + mesh - The DMPlex
2731 . p - The point, which must lie in the chart set with DMPlexSetChart()
2732 - size - The additional cone size for point p
2733 
2734   Output Parameter:
2735 
2736   Note:
2737   This should be called after DMPlexSetChart().
2738 
2739   Level: beginner
2740 
2741 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2742 @*/
2743 PetscErrorCode DMPlexAddConeSize(DM dm, PetscInt p, PetscInt size)
2744 {
2745   DM_Plex       *mesh = (DM_Plex*) dm->data;
2746   PetscFunctionBegin;
2747   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2748   PetscCall(PetscSectionAddDof(mesh->coneSection, p, size));
2749   PetscFunctionReturn(0);
2750 }
2751 
2752 /*@C
2753   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
2754 
2755   Not collective
2756 
2757   Input Parameters:
2758 + dm - The DMPlex
2759 - p - The point, which must lie in the chart set with DMPlexSetChart()
2760 
2761   Output Parameter:
2762 . cone - An array of points which are on the in-edges for point p
2763 
2764   Level: beginner
2765 
2766   Fortran Notes:
2767   Since it returns an array, this routine is only available in Fortran 90, and you must
2768   include petsc.h90 in your code.
2769   You must also call DMPlexRestoreCone() after you finish using the returned array.
2770   DMPlexRestoreCone() is not needed/available in C.
2771 
2772 .seealso: `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`
2773 @*/
2774 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
2775 {
2776   DM_Plex       *mesh = (DM_Plex*) dm->data;
2777   PetscInt       off;
2778 
2779   PetscFunctionBegin;
2780   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2781   PetscValidPointer(cone, 3);
2782   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
2783   *cone = &mesh->cones[off];
2784   PetscFunctionReturn(0);
2785 }
2786 
2787 /*@C
2788   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
2789 
2790   Not collective
2791 
2792   Input Parameters:
2793 + dm - The DMPlex
2794 - p - The IS of points, which must lie in the chart set with DMPlexSetChart()
2795 
2796   Output Parameters:
2797 + pConesSection - PetscSection describing the layout of pCones
2798 - pCones - An array of points which are on the in-edges for the point set p
2799 
2800   Level: intermediate
2801 
2802 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`
2803 @*/
2804 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
2805 {
2806   PetscSection        cs, newcs;
2807   PetscInt            *cones;
2808   PetscInt            *newarr=NULL;
2809   PetscInt            n;
2810 
2811   PetscFunctionBegin;
2812   PetscCall(DMPlexGetCones(dm, &cones));
2813   PetscCall(DMPlexGetConeSection(dm, &cs));
2814   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void**)&newarr) : NULL));
2815   if (pConesSection) *pConesSection = newcs;
2816   if (pCones) {
2817     PetscCall(PetscSectionGetStorageSize(newcs, &n));
2818     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
2819   }
2820   PetscFunctionReturn(0);
2821 }
2822 
2823 /*@
2824   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
2825 
2826   Not collective
2827 
2828   Input Parameters:
2829 + dm - The DMPlex
2830 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2831 
2832   Output Parameter:
2833 . expandedPoints - An array of vertices recursively expanded from input points
2834 
2835   Level: advanced
2836 
2837   Notes:
2838   Like DMPlexGetConeRecursive but returns only the 0-depth IS (i.e. vertices only) and no sections.
2839   There is no corresponding Restore function, just call ISDestroy() on the returned IS to deallocate.
2840 
2841 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetDepth()`
2842 @*/
2843 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
2844 {
2845   IS                  *expandedPointsAll;
2846   PetscInt            depth;
2847 
2848   PetscFunctionBegin;
2849   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2850   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2851   PetscValidPointer(expandedPoints, 3);
2852   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2853   *expandedPoints = expandedPointsAll[0];
2854   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
2855   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2856   PetscFunctionReturn(0);
2857 }
2858 
2859 /*@
2860   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).
2861 
2862   Not collective
2863 
2864   Input Parameters:
2865 + dm - The DMPlex
2866 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2867 
2868   Output Parameters:
2869 + depth - (optional) Size of the output arrays, equal to DMPlex depth, returned by DMPlexGetDepth()
2870 . expandedPoints - (optional) An array of index sets with recursively expanded cones
2871 - sections - (optional) An array of sections which describe mappings from points to their cone points
2872 
2873   Level: advanced
2874 
2875   Notes:
2876   Like DMPlexGetConeTuple() but recursive.
2877 
2878   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.
2879   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
2880 
2881   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:
2882   (1) DAG points in expandedPoints[d+1] with depth d+1 to their cone points in expandedPoints[d];
2883   (2) DAG points in expandedPoints[d+1] with depth in [0,d] to the same points in expandedPoints[d].
2884 
2885 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`, `DMPlexGetDepth()`
2886 @*/
2887 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2888 {
2889   const PetscInt      *arr0=NULL, *cone=NULL;
2890   PetscInt            *arr=NULL, *newarr=NULL;
2891   PetscInt            d, depth_, i, n, newn, cn, co, start, end;
2892   IS                  *expandedPoints_;
2893   PetscSection        *sections_;
2894 
2895   PetscFunctionBegin;
2896   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2897   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2898   if (depth) PetscValidIntPointer(depth, 3);
2899   if (expandedPoints) PetscValidPointer(expandedPoints, 4);
2900   if (sections) PetscValidPointer(sections, 5);
2901   PetscCall(ISGetLocalSize(points, &n));
2902   PetscCall(ISGetIndices(points, &arr0));
2903   PetscCall(DMPlexGetDepth(dm, &depth_));
2904   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
2905   PetscCall(PetscCalloc1(depth_, &sections_));
2906   arr = (PetscInt*) arr0; /* this is ok because first generation of arr is not modified */
2907   for (d=depth_-1; d>=0; d--) {
2908     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
2909     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
2910     for (i=0; i<n; i++) {
2911       PetscCall(DMPlexGetDepthStratum(dm, d+1, &start, &end));
2912       if (arr[i] >= start && arr[i] < end) {
2913         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
2914         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
2915       } else {
2916         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
2917       }
2918     }
2919     PetscCall(PetscSectionSetUp(sections_[d]));
2920     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
2921     PetscCall(PetscMalloc1(newn, &newarr));
2922     for (i=0; i<n; i++) {
2923       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
2924       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
2925       if (cn > 1) {
2926         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
2927         PetscCall(PetscMemcpy(&newarr[co], cone, cn*sizeof(PetscInt)));
2928       } else {
2929         newarr[co] = arr[i];
2930       }
2931     }
2932     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
2933     arr = newarr;
2934     n = newn;
2935   }
2936   PetscCall(ISRestoreIndices(points, &arr0));
2937   *depth = depth_;
2938   if (expandedPoints) *expandedPoints = expandedPoints_;
2939   else {
2940     for (d=0; d<depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
2941     PetscCall(PetscFree(expandedPoints_));
2942   }
2943   if (sections) *sections = sections_;
2944   else {
2945     for (d=0; d<depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
2946     PetscCall(PetscFree(sections_));
2947   }
2948   PetscFunctionReturn(0);
2949 }
2950 
2951 /*@
2952   DMPlexRestoreConeRecursive - Deallocates arrays created by DMPlexGetConeRecursive
2953 
2954   Not collective
2955 
2956   Input Parameters:
2957 + dm - The DMPlex
2958 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2959 
2960   Output Parameters:
2961 + depth - (optional) Size of the output arrays, equal to DMPlex depth, returned by DMPlexGetDepth()
2962 . expandedPoints - (optional) An array of recursively expanded cones
2963 - sections - (optional) An array of sections which describe mappings from points to their cone points
2964 
2965   Level: advanced
2966 
2967   Notes:
2968   See DMPlexGetConeRecursive() for details.
2969 
2970 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`, `DMPlexGetDepth()`
2971 @*/
2972 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2973 {
2974   PetscInt            d, depth_;
2975 
2976   PetscFunctionBegin;
2977   PetscCall(DMPlexGetDepth(dm, &depth_));
2978   PetscCheck(!depth || *depth == depth_,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
2979   if (depth) *depth = 0;
2980   if (expandedPoints) {
2981     for (d=0; d<depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
2982     PetscCall(PetscFree(*expandedPoints));
2983   }
2984   if (sections)  {
2985     for (d=0; d<depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
2986     PetscCall(PetscFree(*sections));
2987   }
2988   PetscFunctionReturn(0);
2989 }
2990 
2991 /*@
2992   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
2993 
2994   Not collective
2995 
2996   Input Parameters:
2997 + mesh - The DMPlex
2998 . p - The point, which must lie in the chart set with DMPlexSetChart()
2999 - cone - An array of points which are on the in-edges for point p
3000 
3001   Output Parameter:
3002 
3003   Note:
3004   This should be called after all calls to DMPlexSetConeSize() and DMSetUp().
3005 
3006   Level: beginner
3007 
3008 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3009 @*/
3010 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3011 {
3012   DM_Plex       *mesh = (DM_Plex*) dm->data;
3013   PetscInt       pStart, pEnd;
3014   PetscInt       dof, off, c;
3015 
3016   PetscFunctionBegin;
3017   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3018   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3019   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3020   if (dof) PetscValidIntPointer(cone, 3);
3021   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3022   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);
3023   for (c = 0; c < dof; ++c) {
3024     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);
3025     mesh->cones[off+c] = cone[c];
3026   }
3027   PetscFunctionReturn(0);
3028 }
3029 
3030 /*@C
3031   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3032 
3033   Not collective
3034 
3035   Input Parameters:
3036 + mesh - The DMPlex
3037 - p - The point, which must lie in the chart set with DMPlexSetChart()
3038 
3039   Output Parameter:
3040 . coneOrientation - An array of orientations which are on the in-edges for point p. An orientation is an
3041                     integer giving the prescription for cone traversal.
3042 
3043   Level: beginner
3044 
3045   Notes:
3046   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3047   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3048   of o, however it is not necessarily the inverse. To get the inverse, use DMPolytopeTypeComposeOrientationInv()
3049   with the identity.
3050 
3051   Fortran Notes:
3052   Since it returns an array, this routine is only available in Fortran 90, and you must
3053   include petsc.h90 in your code.
3054   You must also call DMPlexRestoreConeOrientation() after you finish using the returned array.
3055   DMPlexRestoreConeOrientation() is not needed/available in C.
3056 
3057 .seealso: `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3058 @*/
3059 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3060 {
3061   DM_Plex       *mesh = (DM_Plex*) dm->data;
3062   PetscInt       off;
3063 
3064   PetscFunctionBegin;
3065   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3066   if (PetscDefined(USE_DEBUG)) {
3067     PetscInt dof;
3068     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3069     if (dof) PetscValidPointer(coneOrientation, 3);
3070   }
3071   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3072 
3073   *coneOrientation = &mesh->coneOrientations[off];
3074   PetscFunctionReturn(0);
3075 }
3076 
3077 /*@
3078   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3079 
3080   Not collective
3081 
3082   Input Parameters:
3083 + mesh - The DMPlex
3084 . p - The point, which must lie in the chart set with DMPlexSetChart()
3085 - coneOrientation - An array of orientations
3086   Output Parameter:
3087 
3088   Notes:
3089   This should be called after all calls to DMPlexSetConeSize() and DMSetUp().
3090 
3091   The meaning of coneOrientation is detailed in DMPlexGetConeOrientation().
3092 
3093   Level: beginner
3094 
3095 .seealso: `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3096 @*/
3097 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3098 {
3099   DM_Plex       *mesh = (DM_Plex*) dm->data;
3100   PetscInt       pStart, pEnd;
3101   PetscInt       dof, off, c;
3102 
3103   PetscFunctionBegin;
3104   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3105   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3106   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3107   if (dof) PetscValidIntPointer(coneOrientation, 3);
3108   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3109   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);
3110   for (c = 0; c < dof; ++c) {
3111     PetscInt cdof, o = coneOrientation[c];
3112 
3113     PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off+c], &cdof));
3114     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);
3115     mesh->coneOrientations[off+c] = o;
3116   }
3117   PetscFunctionReturn(0);
3118 }
3119 
3120 /*@
3121   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3122 
3123   Not collective
3124 
3125   Input Parameters:
3126 + mesh - The DMPlex
3127 . p - The point, which must lie in the chart set with DMPlexSetChart()
3128 . conePos - The local index in the cone where the point should be put
3129 - conePoint - The mesh point to insert
3130 
3131   Level: beginner
3132 
3133 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3134 @*/
3135 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3136 {
3137   DM_Plex       *mesh = (DM_Plex*) dm->data;
3138   PetscInt       pStart, pEnd;
3139   PetscInt       dof, off;
3140 
3141   PetscFunctionBegin;
3142   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3143   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3144   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);
3145   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);
3146   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3147   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3148   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);
3149   mesh->cones[off+conePos] = conePoint;
3150   PetscFunctionReturn(0);
3151 }
3152 
3153 /*@
3154   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3155 
3156   Not collective
3157 
3158   Input Parameters:
3159 + mesh - The DMPlex
3160 . p - The point, which must lie in the chart set with DMPlexSetChart()
3161 . conePos - The local index in the cone where the point should be put
3162 - coneOrientation - The point orientation to insert
3163 
3164   Level: beginner
3165 
3166   Notes:
3167   The meaning of coneOrientation values is detailed in DMPlexGetConeOrientation().
3168 
3169 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3170 @*/
3171 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3172 {
3173   DM_Plex       *mesh = (DM_Plex*) dm->data;
3174   PetscInt       pStart, pEnd;
3175   PetscInt       dof, off;
3176 
3177   PetscFunctionBegin;
3178   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3179   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3180   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);
3181   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3182   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3183   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);
3184   mesh->coneOrientations[off+conePos] = coneOrientation;
3185   PetscFunctionReturn(0);
3186 }
3187 
3188 /*@
3189   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3190 
3191   Not collective
3192 
3193   Input Parameters:
3194 + mesh - The DMPlex
3195 - p - The point, which must lie in the chart set with DMPlexSetChart()
3196 
3197   Output Parameter:
3198 . size - The support size for point p
3199 
3200   Level: beginner
3201 
3202 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3203 @*/
3204 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3205 {
3206   DM_Plex       *mesh = (DM_Plex*) dm->data;
3207 
3208   PetscFunctionBegin;
3209   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3210   PetscValidIntPointer(size, 3);
3211   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3212   PetscFunctionReturn(0);
3213 }
3214 
3215 /*@
3216   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3217 
3218   Not collective
3219 
3220   Input Parameters:
3221 + mesh - The DMPlex
3222 . p - The point, which must lie in the chart set with DMPlexSetChart()
3223 - size - The support size for point p
3224 
3225   Output Parameter:
3226 
3227   Note:
3228   This should be called after DMPlexSetChart().
3229 
3230   Level: beginner
3231 
3232 .seealso: `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3233 @*/
3234 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3235 {
3236   DM_Plex       *mesh = (DM_Plex*) dm->data;
3237 
3238   PetscFunctionBegin;
3239   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3240   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3241   PetscFunctionReturn(0);
3242 }
3243 
3244 /*@C
3245   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3246 
3247   Not collective
3248 
3249   Input Parameters:
3250 + mesh - The DMPlex
3251 - p - The point, which must lie in the chart set with DMPlexSetChart()
3252 
3253   Output Parameter:
3254 . support - An array of points which are on the out-edges for point p
3255 
3256   Level: beginner
3257 
3258   Fortran Notes:
3259   Since it returns an array, this routine is only available in Fortran 90, and you must
3260   include petsc.h90 in your code.
3261   You must also call DMPlexRestoreSupport() after you finish using the returned array.
3262   DMPlexRestoreSupport() is not needed/available in C.
3263 
3264 .seealso: `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3265 @*/
3266 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3267 {
3268   DM_Plex       *mesh = (DM_Plex*) dm->data;
3269   PetscInt       off;
3270 
3271   PetscFunctionBegin;
3272   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3273   PetscValidPointer(support, 3);
3274   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3275   *support = &mesh->supports[off];
3276   PetscFunctionReturn(0);
3277 }
3278 
3279 /*@
3280   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3281 
3282   Not collective
3283 
3284   Input Parameters:
3285 + mesh - The DMPlex
3286 . p - The point, which must lie in the chart set with DMPlexSetChart()
3287 - support - An array of points which are on the out-edges for point p
3288 
3289   Output Parameter:
3290 
3291   Note:
3292   This should be called after all calls to DMPlexSetSupportSize() and DMSetUp().
3293 
3294   Level: beginner
3295 
3296 .seealso: `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3297 @*/
3298 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3299 {
3300   DM_Plex       *mesh = (DM_Plex*) dm->data;
3301   PetscInt       pStart, pEnd;
3302   PetscInt       dof, off, c;
3303 
3304   PetscFunctionBegin;
3305   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3306   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3307   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3308   if (dof) PetscValidIntPointer(support, 3);
3309   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3310   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);
3311   for (c = 0; c < dof; ++c) {
3312     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);
3313     mesh->supports[off+c] = support[c];
3314   }
3315   PetscFunctionReturn(0);
3316 }
3317 
3318 /*@
3319   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3320 
3321   Not collective
3322 
3323   Input Parameters:
3324 + mesh - The DMPlex
3325 . p - The point, which must lie in the chart set with DMPlexSetChart()
3326 . supportPos - The local index in the cone where the point should be put
3327 - supportPoint - The mesh point to insert
3328 
3329   Level: beginner
3330 
3331 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3332 @*/
3333 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3334 {
3335   DM_Plex       *mesh = (DM_Plex*) dm->data;
3336   PetscInt       pStart, pEnd;
3337   PetscInt       dof, off;
3338 
3339   PetscFunctionBegin;
3340   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3341   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3342   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3343   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3344   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);
3345   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);
3346   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);
3347   mesh->supports[off+supportPos] = supportPoint;
3348   PetscFunctionReturn(0);
3349 }
3350 
3351 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3352 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3353 {
3354   switch (ct) {
3355     case DM_POLYTOPE_SEGMENT:
3356       if (o == -1) return -2;
3357       break;
3358     case DM_POLYTOPE_TRIANGLE:
3359       if (o == -3) return -1;
3360       if (o == -2) return -3;
3361       if (o == -1) return -2;
3362       break;
3363     case DM_POLYTOPE_QUADRILATERAL:
3364       if (o == -4) return -2;
3365       if (o == -3) return -1;
3366       if (o == -2) return -4;
3367       if (o == -1) return -3;
3368       break;
3369     default: return o;
3370   }
3371   return o;
3372 }
3373 
3374 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3375 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3376 {
3377   switch (ct) {
3378     case DM_POLYTOPE_SEGMENT:
3379       if ((o == -2) || (o == 1)) return -1;
3380       if (o == -1) return 0;
3381       break;
3382     case DM_POLYTOPE_TRIANGLE:
3383       if (o == -3) return -2;
3384       if (o == -2) return -1;
3385       if (o == -1) return -3;
3386       break;
3387     case DM_POLYTOPE_QUADRILATERAL:
3388       if (o == -4) return -2;
3389       if (o == -3) return -1;
3390       if (o == -2) return -4;
3391       if (o == -1) return -3;
3392       break;
3393     default: return o;
3394   }
3395   return o;
3396 }
3397 
3398 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3399 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3400 {
3401   PetscInt       pStart, pEnd, p;
3402 
3403   PetscFunctionBegin;
3404   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3405   for (p = pStart; p < pEnd; ++p) {
3406     const PetscInt *cone, *ornt;
3407     PetscInt        coneSize, c;
3408 
3409     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3410     PetscCall(DMPlexGetCone(dm, p, &cone));
3411     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3412     for (c = 0; c < coneSize; ++c) {
3413       DMPolytopeType ct;
3414       const PetscInt o = ornt[c];
3415 
3416       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3417       switch (ct) {
3418         case DM_POLYTOPE_SEGMENT:
3419           if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3420           if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3421           break;
3422         case DM_POLYTOPE_TRIANGLE:
3423           if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3424           if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3425           if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3426           break;
3427         case DM_POLYTOPE_QUADRILATERAL:
3428           if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3429           if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3430           if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3431           if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3432           break;
3433         default: break;
3434       }
3435     }
3436   }
3437   PetscFunctionReturn(0);
3438 }
3439 
3440 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3441 {
3442   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3443   PetscInt       *closure;
3444   const PetscInt *tmp = NULL, *tmpO = NULL;
3445   PetscInt        off = 0, tmpSize, t;
3446 
3447   PetscFunctionBeginHot;
3448   if (ornt) {
3449     PetscCall(DMPlexGetCellType(dm, p, &ct));
3450     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3451   }
3452   if (*points) {
3453     closure = *points;
3454   } else {
3455     PetscInt maxConeSize, maxSupportSize;
3456     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3457     PetscCall(DMGetWorkArray(dm, 2*(PetscMax(maxConeSize, maxSupportSize)+1), MPIU_INT, &closure));
3458   }
3459   if (useCone) {
3460     PetscCall(DMPlexGetConeSize(dm, p, &tmpSize));
3461     PetscCall(DMPlexGetCone(dm, p, &tmp));
3462     PetscCall(DMPlexGetConeOrientation(dm, p, &tmpO));
3463   } else {
3464     PetscCall(DMPlexGetSupportSize(dm, p, &tmpSize));
3465     PetscCall(DMPlexGetSupport(dm, p, &tmp));
3466   }
3467   if (ct == DM_POLYTOPE_UNKNOWN) {
3468     closure[off++] = p;
3469     closure[off++] = 0;
3470     for (t = 0; t < tmpSize; ++t) {
3471       closure[off++] = tmp[t];
3472       closure[off++] = tmpO ? tmpO[t] : 0;
3473     }
3474   } else {
3475     const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, ornt);
3476 
3477     /* We assume that cells with a valid type have faces with a valid type */
3478     closure[off++] = p;
3479     closure[off++] = ornt;
3480     for (t = 0; t < tmpSize; ++t) {
3481       DMPolytopeType ft;
3482 
3483       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3484       closure[off++] = tmp[arr[t]];
3485       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3486     }
3487   }
3488   if (numPoints) *numPoints = tmpSize+1;
3489   if (points)    *points    = closure;
3490   PetscFunctionReturn(0);
3491 }
3492 
3493 /* We need a special tensor verison becasue we want to allow duplicate points in the endcaps for hybrid cells */
3494 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3495 {
3496   const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, o);
3497   const PetscInt *cone, *ornt;
3498   PetscInt       *pts,  *closure = NULL;
3499   DMPolytopeType  ft;
3500   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3501   PetscInt        dim, coneSize, c, d, clSize, cl;
3502 
3503   PetscFunctionBeginHot;
3504   PetscCall(DMGetDimension(dm, &dim));
3505   PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
3506   PetscCall(DMPlexGetCone(dm, point, &cone));
3507   PetscCall(DMPlexGetConeOrientation(dm, point, &ornt));
3508   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3509   coneSeries    = (maxConeSize    > 1) ? ((PetscPowInt(maxConeSize,    dim+1)-1)/(maxConeSize-1))    : dim+1;
3510   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim+1)-1)/(maxSupportSize-1)) : dim+1;
3511   maxSize       = PetscMax(coneSeries, supportSeries);
3512   if (*points) {pts  = *points;}
3513   else         PetscCall(DMGetWorkArray(dm, 2*maxSize, MPIU_INT, &pts));
3514   c    = 0;
3515   pts[c++] = point;
3516   pts[c++] = o;
3517   PetscCall(DMPlexGetCellType(dm, cone[arr[0*2+0]], &ft));
3518   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0*2+0]], DMPolytopeTypeComposeOrientation(ft, arr[0*2+1], ornt[0]), useCone, &clSize, &closure));
3519   for (cl = 0; cl < clSize*2; cl += 2) {pts[c++] = closure[cl]; pts[c++] = closure[cl+1];}
3520   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1*2+0]], DMPolytopeTypeComposeOrientation(ft, arr[1*2+1], ornt[1]), useCone, &clSize, &closure));
3521   for (cl = 0; cl < clSize*2; cl += 2) {pts[c++] = closure[cl]; pts[c++] = closure[cl+1];}
3522   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3523   for (d = 2; d < coneSize; ++d) {
3524     PetscCall(DMPlexGetCellType(dm, cone[arr[d*2+0]], &ft));
3525     pts[c++] = cone[arr[d*2+0]];
3526     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d*2+1], ornt[d]);
3527   }
3528   if (dim >= 3) {
3529     for (d = 2; d < coneSize; ++d) {
3530       const PetscInt  fpoint = cone[arr[d*2+0]];
3531       const PetscInt *fcone, *fornt;
3532       PetscInt        fconeSize, fc, i;
3533 
3534       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3535       const PetscInt *farr = DMPolytopeTypeGetArrangment(ft, DMPolytopeTypeComposeOrientation(ft, arr[d*2+1], ornt[d]));
3536       PetscCall(DMPlexGetConeSize(dm, fpoint, &fconeSize));
3537       PetscCall(DMPlexGetCone(dm, fpoint, &fcone));
3538       PetscCall(DMPlexGetConeOrientation(dm, fpoint, &fornt));
3539       for (fc = 0; fc < fconeSize; ++fc) {
3540         const PetscInt cp = fcone[farr[fc*2+0]];
3541         const PetscInt co = farr[fc*2+1];
3542 
3543         for (i = 0; i < c; i += 2) if (pts[i] == cp) break;
3544         if (i == c) {
3545           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3546           pts[c++] = cp;
3547           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc*2+0]]);
3548         }
3549       }
3550     }
3551   }
3552   *numPoints = c/2;
3553   *points    = pts;
3554   PetscFunctionReturn(0);
3555 }
3556 
3557 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3558 {
3559   DMPolytopeType ct;
3560   PetscInt      *closure, *fifo;
3561   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3562   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3563   PetscInt       depth, maxSize;
3564 
3565   PetscFunctionBeginHot;
3566   PetscCall(DMPlexGetDepth(dm, &depth));
3567   if (depth == 1) {
3568     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3569     PetscFunctionReturn(0);
3570   }
3571   PetscCall(DMPlexGetCellType(dm, p, &ct));
3572   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3573   if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
3574     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3575     PetscFunctionReturn(0);
3576   }
3577   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3578   coneSeries    = (maxConeSize    > 1) ? ((PetscPowInt(maxConeSize,    depth+1)-1)/(maxConeSize-1))    : depth+1;
3579   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth+1)-1)/(maxSupportSize-1)) : depth+1;
3580   maxSize       = PetscMax(coneSeries, supportSeries);
3581   PetscCall(DMGetWorkArray(dm, 3*maxSize, MPIU_INT, &fifo));
3582   if (*points) {closure = *points;}
3583   else         PetscCall(DMGetWorkArray(dm, 2*maxSize, MPIU_INT, &closure));
3584   closure[closureSize++] = p;
3585   closure[closureSize++] = ornt;
3586   fifo[fifoSize++]       = p;
3587   fifo[fifoSize++]       = ornt;
3588   fifo[fifoSize++]       = ct;
3589   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3590   while (fifoSize - fifoStart) {
3591     const PetscInt       q    = fifo[fifoStart++];
3592     const PetscInt       o    = fifo[fifoStart++];
3593     const DMPolytopeType qt   = (DMPolytopeType) fifo[fifoStart++];
3594     const PetscInt      *qarr = DMPolytopeTypeGetArrangment(qt, o);
3595     const PetscInt      *tmp, *tmpO;
3596     PetscInt             tmpSize, t;
3597 
3598     if (PetscDefined(USE_DEBUG)) {
3599       PetscInt nO = DMPolytopeTypeGetNumArrangments(qt)/2;
3600       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);
3601     }
3602     if (useCone) {
3603       PetscCall(DMPlexGetConeSize(dm, q, &tmpSize));
3604       PetscCall(DMPlexGetCone(dm, q, &tmp));
3605       PetscCall(DMPlexGetConeOrientation(dm, q, &tmpO));
3606     } else {
3607       PetscCall(DMPlexGetSupportSize(dm, q, &tmpSize));
3608       PetscCall(DMPlexGetSupport(dm, q, &tmp));
3609       tmpO = NULL;
3610     }
3611     for (t = 0; t < tmpSize; ++t) {
3612       const PetscInt ip = useCone && qarr ? qarr[t*2]   : t;
3613       const PetscInt io = useCone && qarr ? qarr[t*2+1] : 0;
3614       const PetscInt cp = tmp[ip];
3615       PetscCall(DMPlexGetCellType(dm, cp, &ct));
3616       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
3617       PetscInt       c;
3618 
3619       /* Check for duplicate */
3620       for (c = 0; c < closureSize; c += 2) {
3621         if (closure[c] == cp) break;
3622       }
3623       if (c == closureSize) {
3624         closure[closureSize++] = cp;
3625         closure[closureSize++] = co;
3626         fifo[fifoSize++]       = cp;
3627         fifo[fifoSize++]       = co;
3628         fifo[fifoSize++]       = ct;
3629       }
3630     }
3631   }
3632   PetscCall(DMRestoreWorkArray(dm, 3*maxSize, MPIU_INT, &fifo));
3633   if (numPoints) *numPoints = closureSize/2;
3634   if (points)    *points    = closure;
3635   PetscFunctionReturn(0);
3636 }
3637 
3638 /*@C
3639   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
3640 
3641   Not collective
3642 
3643   Input Parameters:
3644 + dm      - The DMPlex
3645 . p       - The mesh point
3646 - useCone - PETSC_TRUE for the closure, otherwise return the star
3647 
3648   Input/Output Parameter:
3649 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
3650            if NULL on input, internal storage will be returned, otherwise the provided array is used
3651 
3652   Output Parameter:
3653 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3654 
3655   Note:
3656   If using internal storage (points is NULL on input), each call overwrites the last output.
3657 
3658   Fortran Notes:
3659   Since it returns an array, this routine is only available in Fortran 90, and you must include petsc.h90 in your code.
3660 
3661   The numPoints argument is not present in the Fortran 90 binding since it is internal to the array.
3662 
3663   Level: beginner
3664 
3665 .seealso: `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3666 @*/
3667 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3668 {
3669   PetscFunctionBeginHot;
3670   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3671   if (numPoints) PetscValidIntPointer(numPoints, 4);
3672   if (points)    PetscValidPointer(points, 5);
3673   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
3674   PetscFunctionReturn(0);
3675 }
3676 
3677 /*@C
3678   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
3679 
3680   Not collective
3681 
3682   Input Parameters:
3683 + dm        - The DMPlex
3684 . p         - The mesh point
3685 . useCone   - PETSC_TRUE for the closure, otherwise return the star
3686 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3687 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
3688 
3689   Note:
3690   If not using internal storage (points is not NULL on input), this call is unnecessary
3691 
3692   Fortran Notes:
3693   Since it returns an array, this routine is only available in Fortran 90, and you must include petsc.h90 in your code.
3694 
3695   The numPoints argument is not present in the Fortran 90 binding since it is internal to the array.
3696 
3697   Level: beginner
3698 
3699 .seealso: `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3700 @*/
3701 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3702 {
3703   PetscFunctionBeginHot;
3704   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3705   if (numPoints) *numPoints = 0;
3706   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
3707   PetscFunctionReturn(0);
3708 }
3709 
3710 /*@
3711   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
3712 
3713   Not collective
3714 
3715   Input Parameter:
3716 . mesh - The DMPlex
3717 
3718   Output Parameters:
3719 + maxConeSize - The maximum number of in-edges
3720 - maxSupportSize - The maximum number of out-edges
3721 
3722   Level: beginner
3723 
3724 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3725 @*/
3726 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
3727 {
3728   DM_Plex *mesh = (DM_Plex*) dm->data;
3729 
3730   PetscFunctionBegin;
3731   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3732   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
3733   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
3734   PetscFunctionReturn(0);
3735 }
3736 
3737 PetscErrorCode DMSetUp_Plex(DM dm)
3738 {
3739   DM_Plex       *mesh = (DM_Plex*) dm->data;
3740   PetscInt       size, maxSupportSize;
3741 
3742   PetscFunctionBegin;
3743   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3744   PetscCall(PetscSectionSetUp(mesh->coneSection));
3745   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
3746   PetscCall(PetscMalloc1(size, &mesh->cones));
3747   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
3748   PetscCall(PetscLogObjectMemory((PetscObject) dm, size*2*sizeof(PetscInt)));
3749   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
3750   if (maxSupportSize) {
3751     PetscCall(PetscSectionSetUp(mesh->supportSection));
3752     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
3753     PetscCall(PetscMalloc1(size, &mesh->supports));
3754     PetscCall(PetscLogObjectMemory((PetscObject) dm, size*sizeof(PetscInt)));
3755   }
3756   PetscFunctionReturn(0);
3757 }
3758 
3759 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
3760 {
3761   PetscFunctionBegin;
3762   if (subdm) PetscCall(DMClone(dm, subdm));
3763   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, is, subdm));
3764   if (subdm) {(*subdm)->useNatural = dm->useNatural;}
3765   if (dm->useNatural && dm->sfMigration) {
3766     PetscSF        sfMigrationInv,sfNatural;
3767     PetscSection   section, sectionSeq;
3768 
3769     (*subdm)->sfMigration = dm->sfMigration;
3770     PetscCall(PetscObjectReference((PetscObject) dm->sfMigration));
3771     PetscCall(DMGetLocalSection((*subdm), &section));
3772     PetscCall(PetscSFCreateInverseSF((*subdm)->sfMigration, &sfMigrationInv));
3773     PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject) (*subdm)), &sectionSeq));
3774     PetscCall(PetscSFDistributeSection(sfMigrationInv, section, NULL, sectionSeq));
3775 
3776     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, sectionSeq, (*subdm)->sfMigration, &sfNatural));
3777     (*subdm)->sfNatural = sfNatural;
3778     PetscCall(PetscSectionDestroy(&sectionSeq));
3779     PetscCall(PetscSFDestroy(&sfMigrationInv));
3780   }
3781   PetscFunctionReturn(0);
3782 }
3783 
3784 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
3785 {
3786   PetscInt       i = 0;
3787 
3788   PetscFunctionBegin;
3789   PetscCall(DMClone(dms[0], superdm));
3790   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
3791   (*superdm)->useNatural = PETSC_FALSE;
3792   for (i = 0; i < len; i++) {
3793     if (dms[i]->useNatural && dms[i]->sfMigration) {
3794       PetscSF        sfMigrationInv,sfNatural;
3795       PetscSection   section, sectionSeq;
3796 
3797       (*superdm)->sfMigration = dms[i]->sfMigration;
3798       PetscCall(PetscObjectReference((PetscObject) dms[i]->sfMigration));
3799       (*superdm)->useNatural = PETSC_TRUE;
3800       PetscCall(DMGetLocalSection((*superdm), &section));
3801       PetscCall(PetscSFCreateInverseSF((*superdm)->sfMigration, &sfMigrationInv));
3802       PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject) (*superdm)), &sectionSeq));
3803       PetscCall(PetscSFDistributeSection(sfMigrationInv, section, NULL, sectionSeq));
3804 
3805       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, sectionSeq, (*superdm)->sfMigration, &sfNatural));
3806       (*superdm)->sfNatural = sfNatural;
3807       PetscCall(PetscSectionDestroy(&sectionSeq));
3808       PetscCall(PetscSFDestroy(&sfMigrationInv));
3809       break;
3810     }
3811   }
3812   PetscFunctionReturn(0);
3813 }
3814 
3815 /*@
3816   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
3817 
3818   Not collective
3819 
3820   Input Parameter:
3821 . mesh - The DMPlex
3822 
3823   Output Parameter:
3824 
3825   Note:
3826   This should be called after all calls to DMPlexSetCone()
3827 
3828   Level: beginner
3829 
3830 .seealso: `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
3831 @*/
3832 PetscErrorCode DMPlexSymmetrize(DM dm)
3833 {
3834   DM_Plex       *mesh = (DM_Plex*) dm->data;
3835   PetscInt      *offsets;
3836   PetscInt       supportSize;
3837   PetscInt       pStart, pEnd, p;
3838 
3839   PetscFunctionBegin;
3840   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3841   PetscCheck(!mesh->supports,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
3842   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize,dm,0,0,0));
3843   /* Calculate support sizes */
3844   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3845   for (p = pStart; p < pEnd; ++p) {
3846     PetscInt dof, off, c;
3847 
3848     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3849     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3850     for (c = off; c < off+dof; ++c) {
3851       PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
3852     }
3853   }
3854   PetscCall(PetscSectionSetUp(mesh->supportSection));
3855   /* Calculate supports */
3856   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
3857   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
3858   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
3859   for (p = pStart; p < pEnd; ++p) {
3860     PetscInt dof, off, c;
3861 
3862     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3863     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3864     for (c = off; c < off+dof; ++c) {
3865       const PetscInt q = mesh->cones[c];
3866       PetscInt       offS;
3867 
3868       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
3869 
3870       mesh->supports[offS+offsets[q]] = p;
3871       ++offsets[q];
3872     }
3873   }
3874   PetscCall(PetscFree(offsets));
3875   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize,dm,0,0,0));
3876   PetscFunctionReturn(0);
3877 }
3878 
3879 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
3880 {
3881   IS             stratumIS;
3882 
3883   PetscFunctionBegin;
3884   if (pStart >= pEnd) PetscFunctionReturn(0);
3885   if (PetscDefined(USE_DEBUG)) {
3886     PetscInt  qStart, qEnd, numLevels, level;
3887     PetscBool overlap = PETSC_FALSE;
3888     PetscCall(DMLabelGetNumValues(label, &numLevels));
3889     for (level = 0; level < numLevels; level++) {
3890       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
3891       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {overlap = PETSC_TRUE; break;}
3892     }
3893     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);
3894   }
3895   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd-pStart, pStart, 1, &stratumIS));
3896   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
3897   PetscCall(ISDestroy(&stratumIS));
3898   PetscFunctionReturn(0);
3899 }
3900 
3901 /*@
3902   DMPlexStratify - The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
3903   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram). The strata group all points of the
3904   same grade, and this function calculates the strata. This grade can be seen as the height (or depth) of the point in
3905   the DAG.
3906 
3907   Collective on dm
3908 
3909   Input Parameter:
3910 . mesh - The DMPlex
3911 
3912   Output Parameter:
3913 
3914   Notes:
3915   Concretely, DMPlexStratify() creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
3916   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
3917   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through DMPlexGetDepthLabel() or DMPlexGetDepthStratum(), or
3918   manually via DMGetLabel().  The height is defined implicitly by height = maxDimension - depth, and can be accessed
3919   via DMPlexGetHeightStratum().  For example, cells have height 0 and faces have height 1.
3920 
3921   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
3922   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
3923   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
3924   to interpolate only that one (e0), so that
3925 $  cone(c0) = {e0, v2}
3926 $  cone(e0) = {v0, v1}
3927   If DMPlexStratify() is run on this mesh, it will give depths
3928 $  depth 0 = {v0, v1, v2}
3929 $  depth 1 = {e0, c0}
3930   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
3931 
3932   DMPlexStratify() should be called after all calls to DMPlexSymmetrize()
3933 
3934   Level: beginner
3935 
3936 .seealso: `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
3937 @*/
3938 PetscErrorCode DMPlexStratify(DM dm)
3939 {
3940   DM_Plex       *mesh = (DM_Plex*) dm->data;
3941   DMLabel        label;
3942   PetscInt       pStart, pEnd, p;
3943   PetscInt       numRoots = 0, numLeaves = 0;
3944 
3945   PetscFunctionBegin;
3946   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3947   PetscCall(PetscLogEventBegin(DMPLEX_Stratify,dm,0,0,0));
3948 
3949   /* Create depth label */
3950   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3951   PetscCall(DMCreateLabel(dm, "depth"));
3952   PetscCall(DMPlexGetDepthLabel(dm, &label));
3953 
3954   {
3955     /* Initialize roots and count leaves */
3956     PetscInt sMin = PETSC_MAX_INT;
3957     PetscInt sMax = PETSC_MIN_INT;
3958     PetscInt coneSize, supportSize;
3959 
3960     for (p = pStart; p < pEnd; ++p) {
3961       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3962       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
3963       if (!coneSize && supportSize) {
3964         sMin = PetscMin(p, sMin);
3965         sMax = PetscMax(p, sMax);
3966         ++numRoots;
3967       } else if (!supportSize && coneSize) {
3968         ++numLeaves;
3969       } else if (!supportSize && !coneSize) {
3970         /* Isolated points */
3971         sMin = PetscMin(p, sMin);
3972         sMax = PetscMax(p, sMax);
3973       }
3974     }
3975     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax+1));
3976   }
3977 
3978   if (numRoots + numLeaves == (pEnd - pStart)) {
3979     PetscInt sMin = PETSC_MAX_INT;
3980     PetscInt sMax = PETSC_MIN_INT;
3981     PetscInt coneSize, supportSize;
3982 
3983     for (p = pStart; p < pEnd; ++p) {
3984       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3985       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
3986       if (!supportSize && coneSize) {
3987         sMin = PetscMin(p, sMin);
3988         sMax = PetscMax(p, sMax);
3989       }
3990     }
3991     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax+1));
3992   } else {
3993     PetscInt level = 0;
3994     PetscInt qStart, qEnd, q;
3995 
3996     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
3997     while (qEnd > qStart) {
3998       PetscInt sMin = PETSC_MAX_INT;
3999       PetscInt sMax = PETSC_MIN_INT;
4000 
4001       for (q = qStart; q < qEnd; ++q) {
4002         const PetscInt *support;
4003         PetscInt        supportSize, s;
4004 
4005         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4006         PetscCall(DMPlexGetSupport(dm, q, &support));
4007         for (s = 0; s < supportSize; ++s) {
4008           sMin = PetscMin(support[s], sMin);
4009           sMax = PetscMax(support[s], sMax);
4010         }
4011       }
4012       PetscCall(DMLabelGetNumValues(label, &level));
4013       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax+1));
4014       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4015     }
4016   }
4017   { /* just in case there is an empty process */
4018     PetscInt numValues, maxValues = 0, v;
4019 
4020     PetscCall(DMLabelGetNumValues(label, &numValues));
4021     PetscCallMPI(MPI_Allreduce(&numValues,&maxValues,1,MPIU_INT,MPI_MAX,PetscObjectComm((PetscObject)dm)));
4022     for (v = numValues; v < maxValues; v++) {
4023       PetscCall(DMLabelAddStratum(label, v));
4024     }
4025   }
4026   PetscCall(PetscObjectStateGet((PetscObject) label, &mesh->depthState));
4027   PetscCall(PetscLogEventEnd(DMPLEX_Stratify,dm,0,0,0));
4028   PetscFunctionReturn(0);
4029 }
4030 
4031 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4032 {
4033   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4034   PetscInt       dim, depth, pheight, coneSize;
4035 
4036   PetscFunctionBeginHot;
4037   PetscCall(DMGetDimension(dm, &dim));
4038   PetscCall(DMPlexGetDepth(dm, &depth));
4039   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4040   pheight = depth - pdepth;
4041   if (depth <= 1) {
4042     switch (pdepth) {
4043       case 0: ct = DM_POLYTOPE_POINT;break;
4044       case 1:
4045         switch (coneSize) {
4046           case 2: ct = DM_POLYTOPE_SEGMENT;break;
4047           case 3: ct = DM_POLYTOPE_TRIANGLE;break;
4048           case 4:
4049           switch (dim) {
4050             case 2: ct = DM_POLYTOPE_QUADRILATERAL;break;
4051             case 3: ct = DM_POLYTOPE_TETRAHEDRON;break;
4052             default: break;
4053           }
4054           break;
4055         case 5: ct = DM_POLYTOPE_PYRAMID;break;
4056         case 6: ct = DM_POLYTOPE_TRI_PRISM_TENSOR;break;
4057         case 8: ct = DM_POLYTOPE_HEXAHEDRON;break;
4058         default: break;
4059       }
4060     }
4061   } else {
4062     if (pdepth == 0) {
4063       ct = DM_POLYTOPE_POINT;
4064     } else if (pheight == 0) {
4065       switch (dim) {
4066         case 1:
4067           switch (coneSize) {
4068             case 2: ct = DM_POLYTOPE_SEGMENT;break;
4069             default: break;
4070           }
4071           break;
4072         case 2:
4073           switch (coneSize) {
4074             case 3: ct = DM_POLYTOPE_TRIANGLE;break;
4075             case 4: ct = DM_POLYTOPE_QUADRILATERAL;break;
4076             default: break;
4077           }
4078           break;
4079         case 3:
4080           switch (coneSize) {
4081             case 4: ct = DM_POLYTOPE_TETRAHEDRON;break;
4082             case 5:
4083             {
4084               const PetscInt *cone;
4085               PetscInt        faceConeSize;
4086 
4087               PetscCall(DMPlexGetCone(dm, p, &cone));
4088               PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4089               switch (faceConeSize) {
4090                 case 3: ct = DM_POLYTOPE_TRI_PRISM_TENSOR;break;
4091                 case 4: ct = DM_POLYTOPE_PYRAMID;break;
4092               }
4093             }
4094             break;
4095             case 6: ct = DM_POLYTOPE_HEXAHEDRON;break;
4096             default: break;
4097           }
4098           break;
4099         default: break;
4100       }
4101     } else if (pheight > 0) {
4102       switch (coneSize) {
4103         case 2: ct = DM_POLYTOPE_SEGMENT;break;
4104         case 3: ct = DM_POLYTOPE_TRIANGLE;break;
4105         case 4: ct = DM_POLYTOPE_QUADRILATERAL;break;
4106         default: break;
4107       }
4108     }
4109   }
4110   *pt = ct;
4111   PetscFunctionReturn(0);
4112 }
4113 
4114 /*@
4115   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4116 
4117   Collective on dm
4118 
4119   Input Parameter:
4120 . mesh - The DMPlex
4121 
4122   DMPlexComputeCellTypes() should be called after all calls to DMPlexSymmetrize() and DMPlexStratify()
4123 
4124   Level: developer
4125 
4126   Note: This function is normally called automatically by Plex when a cell type is requested. It creates an
4127   internal DMLabel named "celltype" which can be directly accessed using DMGetLabel(). A user may disable
4128   automatic creation by creating the label manually, using DMCreateLabel(dm, "celltype").
4129 
4130 .seealso: `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4131 @*/
4132 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4133 {
4134   DM_Plex       *mesh;
4135   DMLabel        ctLabel;
4136   PetscInt       pStart, pEnd, p;
4137 
4138   PetscFunctionBegin;
4139   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4140   mesh = (DM_Plex *) dm->data;
4141   PetscCall(DMCreateLabel(dm, "celltype"));
4142   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4143   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4144   for (p = pStart; p < pEnd; ++p) {
4145     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4146     PetscInt       pdepth;
4147 
4148     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4149     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4150     PetscCheck(ct != DM_POLYTOPE_UNKNOWN,PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " is screwed up", p);
4151     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4152   }
4153   PetscCall(PetscObjectStateGet((PetscObject) ctLabel, &mesh->celltypeState));
4154   PetscCall(PetscObjectViewFromOptions((PetscObject) ctLabel, NULL, "-dm_plex_celltypes_view"));
4155   PetscFunctionReturn(0);
4156 }
4157 
4158 /*@C
4159   DMPlexGetJoin - Get an array for the join of the set of points
4160 
4161   Not Collective
4162 
4163   Input Parameters:
4164 + dm - The DMPlex object
4165 . numPoints - The number of input points for the join
4166 - points - The input points
4167 
4168   Output Parameters:
4169 + numCoveredPoints - The number of points in the join
4170 - coveredPoints - The points in the join
4171 
4172   Level: intermediate
4173 
4174   Note: Currently, this is restricted to a single level join
4175 
4176   Fortran Notes:
4177   Since it returns an array, this routine is only available in Fortran 90, and you must
4178   include petsc.h90 in your code.
4179 
4180   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4181 
4182 .seealso: `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4183 @*/
4184 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4185 {
4186   DM_Plex       *mesh = (DM_Plex*) dm->data;
4187   PetscInt      *join[2];
4188   PetscInt       joinSize, i = 0;
4189   PetscInt       dof, off, p, c, m;
4190   PetscInt       maxSupportSize;
4191 
4192   PetscFunctionBegin;
4193   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4194   PetscValidIntPointer(points, 3);
4195   PetscValidIntPointer(numCoveredPoints, 4);
4196   PetscValidPointer(coveredPoints, 5);
4197   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4198   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4199   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4200   /* Copy in support of first point */
4201   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4202   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4203   for (joinSize = 0; joinSize < dof; ++joinSize) {
4204     join[i][joinSize] = mesh->supports[off+joinSize];
4205   }
4206   /* Check each successive support */
4207   for (p = 1; p < numPoints; ++p) {
4208     PetscInt newJoinSize = 0;
4209 
4210     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4211     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4212     for (c = 0; c < dof; ++c) {
4213       const PetscInt point = mesh->supports[off+c];
4214 
4215       for (m = 0; m < joinSize; ++m) {
4216         if (point == join[i][m]) {
4217           join[1-i][newJoinSize++] = point;
4218           break;
4219         }
4220       }
4221     }
4222     joinSize = newJoinSize;
4223     i        = 1-i;
4224   }
4225   *numCoveredPoints = joinSize;
4226   *coveredPoints    = join[i];
4227   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1-i]));
4228   PetscFunctionReturn(0);
4229 }
4230 
4231 /*@C
4232   DMPlexRestoreJoin - Restore an array for the join of the set of points
4233 
4234   Not Collective
4235 
4236   Input Parameters:
4237 + dm - The DMPlex object
4238 . numPoints - The number of input points for the join
4239 - points - The input points
4240 
4241   Output Parameters:
4242 + numCoveredPoints - The number of points in the join
4243 - coveredPoints - The points in the join
4244 
4245   Fortran Notes:
4246   Since it returns an array, this routine is only available in Fortran 90, and you must
4247   include petsc.h90 in your code.
4248 
4249   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4250 
4251   Level: intermediate
4252 
4253 .seealso: `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4254 @*/
4255 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4256 {
4257   PetscFunctionBegin;
4258   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4259   if (points) PetscValidIntPointer(points,3);
4260   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints,4);
4261   PetscValidPointer(coveredPoints, 5);
4262   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void*) coveredPoints));
4263   if (numCoveredPoints) *numCoveredPoints = 0;
4264   PetscFunctionReturn(0);
4265 }
4266 
4267 /*@C
4268   DMPlexGetFullJoin - Get an array for the join of the set of points
4269 
4270   Not Collective
4271 
4272   Input Parameters:
4273 + dm - The DMPlex object
4274 . numPoints - The number of input points for the join
4275 - points - The input points
4276 
4277   Output Parameters:
4278 + numCoveredPoints - The number of points in the join
4279 - coveredPoints - The points in the join
4280 
4281   Fortran Notes:
4282   Since it returns an array, this routine is only available in Fortran 90, and you must
4283   include petsc.h90 in your code.
4284 
4285   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4286 
4287   Level: intermediate
4288 
4289 .seealso: `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4290 @*/
4291 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4292 {
4293   PetscInt      *offsets, **closures;
4294   PetscInt      *join[2];
4295   PetscInt       depth = 0, maxSize, joinSize = 0, i = 0;
4296   PetscInt       p, d, c, m, ms;
4297 
4298   PetscFunctionBegin;
4299   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4300   PetscValidIntPointer(points, 3);
4301   PetscValidIntPointer(numCoveredPoints, 4);
4302   PetscValidPointer(coveredPoints, 5);
4303 
4304   PetscCall(DMPlexGetDepth(dm, &depth));
4305   PetscCall(PetscCalloc1(numPoints, &closures));
4306   PetscCall(DMGetWorkArray(dm, numPoints*(depth+2), MPIU_INT, &offsets));
4307   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4308   maxSize = (ms > 1) ? ((PetscPowInt(ms,depth+1)-1)/(ms-1)) : depth + 1;
4309   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4310   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4311 
4312   for (p = 0; p < numPoints; ++p) {
4313     PetscInt closureSize;
4314 
4315     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4316 
4317     offsets[p*(depth+2)+0] = 0;
4318     for (d = 0; d < depth+1; ++d) {
4319       PetscInt pStart, pEnd, i;
4320 
4321       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4322       for (i = offsets[p*(depth+2)+d]; i < closureSize; ++i) {
4323         if ((pStart > closures[p][i*2]) || (pEnd <= closures[p][i*2])) {
4324           offsets[p*(depth+2)+d+1] = i;
4325           break;
4326         }
4327       }
4328       if (i == closureSize) offsets[p*(depth+2)+d+1] = i;
4329     }
4330     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);
4331   }
4332   for (d = 0; d < depth+1; ++d) {
4333     PetscInt dof;
4334 
4335     /* Copy in support of first point */
4336     dof = offsets[d+1] - offsets[d];
4337     for (joinSize = 0; joinSize < dof; ++joinSize) {
4338       join[i][joinSize] = closures[0][(offsets[d]+joinSize)*2];
4339     }
4340     /* Check each successive cone */
4341     for (p = 1; p < numPoints && joinSize; ++p) {
4342       PetscInt newJoinSize = 0;
4343 
4344       dof = offsets[p*(depth+2)+d+1] - offsets[p*(depth+2)+d];
4345       for (c = 0; c < dof; ++c) {
4346         const PetscInt point = closures[p][(offsets[p*(depth+2)+d]+c)*2];
4347 
4348         for (m = 0; m < joinSize; ++m) {
4349           if (point == join[i][m]) {
4350             join[1-i][newJoinSize++] = point;
4351             break;
4352           }
4353         }
4354       }
4355       joinSize = newJoinSize;
4356       i        = 1-i;
4357     }
4358     if (joinSize) break;
4359   }
4360   *numCoveredPoints = joinSize;
4361   *coveredPoints    = join[i];
4362   for (p = 0; p < numPoints; ++p) {
4363     PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4364   }
4365   PetscCall(PetscFree(closures));
4366   PetscCall(DMRestoreWorkArray(dm, numPoints*(depth+2), MPIU_INT, &offsets));
4367   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1-i]));
4368   PetscFunctionReturn(0);
4369 }
4370 
4371 /*@C
4372   DMPlexGetMeet - Get an array for the meet of the set of points
4373 
4374   Not Collective
4375 
4376   Input Parameters:
4377 + dm - The DMPlex object
4378 . numPoints - The number of input points for the meet
4379 - points - The input points
4380 
4381   Output Parameters:
4382 + numCoveredPoints - The number of points in the meet
4383 - coveredPoints - The points in the meet
4384 
4385   Level: intermediate
4386 
4387   Note: Currently, this is restricted to a single level meet
4388 
4389   Fortran Notes:
4390   Since it returns an array, this routine is only available in Fortran 90, and you must
4391   include petsc.h90 in your code.
4392 
4393   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4394 
4395 .seealso: `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4396 @*/
4397 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4398 {
4399   DM_Plex       *mesh = (DM_Plex*) dm->data;
4400   PetscInt      *meet[2];
4401   PetscInt       meetSize, i = 0;
4402   PetscInt       dof, off, p, c, m;
4403   PetscInt       maxConeSize;
4404 
4405   PetscFunctionBegin;
4406   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4407   PetscValidIntPointer(points, 3);
4408   PetscValidIntPointer(numCoveringPoints, 4);
4409   PetscValidPointer(coveringPoints, 5);
4410   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4411   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4412   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4413   /* Copy in cone of first point */
4414   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4415   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4416   for (meetSize = 0; meetSize < dof; ++meetSize) {
4417     meet[i][meetSize] = mesh->cones[off+meetSize];
4418   }
4419   /* Check each successive cone */
4420   for (p = 1; p < numPoints; ++p) {
4421     PetscInt newMeetSize = 0;
4422 
4423     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4424     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4425     for (c = 0; c < dof; ++c) {
4426       const PetscInt point = mesh->cones[off+c];
4427 
4428       for (m = 0; m < meetSize; ++m) {
4429         if (point == meet[i][m]) {
4430           meet[1-i][newMeetSize++] = point;
4431           break;
4432         }
4433       }
4434     }
4435     meetSize = newMeetSize;
4436     i        = 1-i;
4437   }
4438   *numCoveringPoints = meetSize;
4439   *coveringPoints    = meet[i];
4440   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1-i]));
4441   PetscFunctionReturn(0);
4442 }
4443 
4444 /*@C
4445   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4446 
4447   Not Collective
4448 
4449   Input Parameters:
4450 + dm - The DMPlex object
4451 . numPoints - The number of input points for the meet
4452 - points - The input points
4453 
4454   Output Parameters:
4455 + numCoveredPoints - The number of points in the meet
4456 - coveredPoints - The points in the meet
4457 
4458   Level: intermediate
4459 
4460   Fortran Notes:
4461   Since it returns an array, this routine is only available in Fortran 90, and you must
4462   include petsc.h90 in your code.
4463 
4464   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4465 
4466 .seealso: `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4467 @*/
4468 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4469 {
4470   PetscFunctionBegin;
4471   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4472   if (points) PetscValidIntPointer(points,3);
4473   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints,4);
4474   PetscValidPointer(coveredPoints,5);
4475   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void*) coveredPoints));
4476   if (numCoveredPoints) *numCoveredPoints = 0;
4477   PetscFunctionReturn(0);
4478 }
4479 
4480 /*@C
4481   DMPlexGetFullMeet - Get an array for the meet of the set of points
4482 
4483   Not Collective
4484 
4485   Input Parameters:
4486 + dm - The DMPlex object
4487 . numPoints - The number of input points for the meet
4488 - points - The input points
4489 
4490   Output Parameters:
4491 + numCoveredPoints - The number of points in the meet
4492 - coveredPoints - The points in the meet
4493 
4494   Level: intermediate
4495 
4496   Fortran Notes:
4497   Since it returns an array, this routine is only available in Fortran 90, and you must
4498   include petsc.h90 in your code.
4499 
4500   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4501 
4502 .seealso: `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4503 @*/
4504 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4505 {
4506   PetscInt      *offsets, **closures;
4507   PetscInt      *meet[2];
4508   PetscInt       height = 0, maxSize, meetSize = 0, i = 0;
4509   PetscInt       p, h, c, m, mc;
4510 
4511   PetscFunctionBegin;
4512   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4513   PetscValidIntPointer(points, 3);
4514   PetscValidIntPointer(numCoveredPoints, 4);
4515   PetscValidPointer(coveredPoints, 5);
4516 
4517   PetscCall(DMPlexGetDepth(dm, &height));
4518   PetscCall(PetscMalloc1(numPoints, &closures));
4519   PetscCall(DMGetWorkArray(dm, numPoints*(height+2), MPIU_INT, &offsets));
4520   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4521   maxSize = (mc > 1) ? ((PetscPowInt(mc,height+1)-1)/(mc-1)) : height + 1;
4522   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4523   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4524 
4525   for (p = 0; p < numPoints; ++p) {
4526     PetscInt closureSize;
4527 
4528     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4529 
4530     offsets[p*(height+2)+0] = 0;
4531     for (h = 0; h < height+1; ++h) {
4532       PetscInt pStart, pEnd, i;
4533 
4534       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4535       for (i = offsets[p*(height+2)+h]; i < closureSize; ++i) {
4536         if ((pStart > closures[p][i*2]) || (pEnd <= closures[p][i*2])) {
4537           offsets[p*(height+2)+h+1] = i;
4538           break;
4539         }
4540       }
4541       if (i == closureSize) offsets[p*(height+2)+h+1] = i;
4542     }
4543     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);
4544   }
4545   for (h = 0; h < height+1; ++h) {
4546     PetscInt dof;
4547 
4548     /* Copy in cone of first point */
4549     dof = offsets[h+1] - offsets[h];
4550     for (meetSize = 0; meetSize < dof; ++meetSize) {
4551       meet[i][meetSize] = closures[0][(offsets[h]+meetSize)*2];
4552     }
4553     /* Check each successive cone */
4554     for (p = 1; p < numPoints && meetSize; ++p) {
4555       PetscInt newMeetSize = 0;
4556 
4557       dof = offsets[p*(height+2)+h+1] - offsets[p*(height+2)+h];
4558       for (c = 0; c < dof; ++c) {
4559         const PetscInt point = closures[p][(offsets[p*(height+2)+h]+c)*2];
4560 
4561         for (m = 0; m < meetSize; ++m) {
4562           if (point == meet[i][m]) {
4563             meet[1-i][newMeetSize++] = point;
4564             break;
4565           }
4566         }
4567       }
4568       meetSize = newMeetSize;
4569       i        = 1-i;
4570     }
4571     if (meetSize) break;
4572   }
4573   *numCoveredPoints = meetSize;
4574   *coveredPoints    = meet[i];
4575   for (p = 0; p < numPoints; ++p) {
4576     PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
4577   }
4578   PetscCall(PetscFree(closures));
4579   PetscCall(DMRestoreWorkArray(dm, numPoints*(height+2), MPIU_INT, &offsets));
4580   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1-i]));
4581   PetscFunctionReturn(0);
4582 }
4583 
4584 /*@C
4585   DMPlexEqual - Determine if two DMs have the same topology
4586 
4587   Not Collective
4588 
4589   Input Parameters:
4590 + dmA - A DMPlex object
4591 - dmB - A DMPlex object
4592 
4593   Output Parameters:
4594 . equal - PETSC_TRUE if the topologies are identical
4595 
4596   Level: intermediate
4597 
4598   Notes:
4599   We are not solving graph isomorphism, so we do not permutation.
4600 
4601 .seealso: `DMPlexGetCone()`
4602 @*/
4603 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
4604 {
4605   PetscInt       depth, depthB, pStart, pEnd, pStartB, pEndB, p;
4606 
4607   PetscFunctionBegin;
4608   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
4609   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
4610   PetscValidBoolPointer(equal, 3);
4611 
4612   *equal = PETSC_FALSE;
4613   PetscCall(DMPlexGetDepth(dmA, &depth));
4614   PetscCall(DMPlexGetDepth(dmB, &depthB));
4615   if (depth != depthB) PetscFunctionReturn(0);
4616   PetscCall(DMPlexGetChart(dmA, &pStart,  &pEnd));
4617   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
4618   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(0);
4619   for (p = pStart; p < pEnd; ++p) {
4620     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
4621     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
4622 
4623     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
4624     PetscCall(DMPlexGetCone(dmA, p, &cone));
4625     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
4626     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
4627     PetscCall(DMPlexGetCone(dmB, p, &coneB));
4628     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
4629     if (coneSize != coneSizeB) PetscFunctionReturn(0);
4630     for (c = 0; c < coneSize; ++c) {
4631       if (cone[c] != coneB[c]) PetscFunctionReturn(0);
4632       if (ornt[c] != orntB[c]) PetscFunctionReturn(0);
4633     }
4634     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
4635     PetscCall(DMPlexGetSupport(dmA, p, &support));
4636     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
4637     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
4638     if (supportSize != supportSizeB) PetscFunctionReturn(0);
4639     for (s = 0; s < supportSize; ++s) {
4640       if (support[s] != supportB[s]) PetscFunctionReturn(0);
4641     }
4642   }
4643   *equal = PETSC_TRUE;
4644   PetscFunctionReturn(0);
4645 }
4646 
4647 /*@C
4648   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
4649 
4650   Not Collective
4651 
4652   Input Parameters:
4653 + dm         - The DMPlex
4654 . cellDim    - The cell dimension
4655 - numCorners - The number of vertices on a cell
4656 
4657   Output Parameters:
4658 . numFaceVertices - The number of vertices on a face
4659 
4660   Level: developer
4661 
4662   Notes:
4663   Of course this can only work for a restricted set of symmetric shapes
4664 
4665 .seealso: `DMPlexGetCone()`
4666 @*/
4667 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
4668 {
4669   MPI_Comm       comm;
4670 
4671   PetscFunctionBegin;
4672   PetscCall(PetscObjectGetComm((PetscObject)dm,&comm));
4673   PetscValidIntPointer(numFaceVertices,4);
4674   switch (cellDim) {
4675   case 0:
4676     *numFaceVertices = 0;
4677     break;
4678   case 1:
4679     *numFaceVertices = 1;
4680     break;
4681   case 2:
4682     switch (numCorners) {
4683     case 3: /* triangle */
4684       *numFaceVertices = 2; /* Edge has 2 vertices */
4685       break;
4686     case 4: /* quadrilateral */
4687       *numFaceVertices = 2; /* Edge has 2 vertices */
4688       break;
4689     case 6: /* quadratic triangle, tri and quad cohesive Lagrange cells */
4690       *numFaceVertices = 3; /* Edge has 3 vertices */
4691       break;
4692     case 9: /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
4693       *numFaceVertices = 3; /* Edge has 3 vertices */
4694       break;
4695     default:
4696       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4697     }
4698     break;
4699   case 3:
4700     switch (numCorners) {
4701     case 4: /* tetradehdron */
4702       *numFaceVertices = 3; /* Face has 3 vertices */
4703       break;
4704     case 6: /* tet cohesive cells */
4705       *numFaceVertices = 4; /* Face has 4 vertices */
4706       break;
4707     case 8: /* hexahedron */
4708       *numFaceVertices = 4; /* Face has 4 vertices */
4709       break;
4710     case 9: /* tet cohesive Lagrange cells */
4711       *numFaceVertices = 6; /* Face has 6 vertices */
4712       break;
4713     case 10: /* quadratic tetrahedron */
4714       *numFaceVertices = 6; /* Face has 6 vertices */
4715       break;
4716     case 12: /* hex cohesive Lagrange cells */
4717       *numFaceVertices = 6; /* Face has 6 vertices */
4718       break;
4719     case 18: /* quadratic tet cohesive Lagrange cells */
4720       *numFaceVertices = 6; /* Face has 6 vertices */
4721       break;
4722     case 27: /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
4723       *numFaceVertices = 9; /* Face has 9 vertices */
4724       break;
4725     default:
4726       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4727     }
4728     break;
4729   default:
4730     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
4731   }
4732   PetscFunctionReturn(0);
4733 }
4734 
4735 /*@
4736   DMPlexGetDepthLabel - Get the DMLabel recording the depth of each point
4737 
4738   Not Collective
4739 
4740   Input Parameter:
4741 . dm    - The DMPlex object
4742 
4743   Output Parameter:
4744 . depthLabel - The DMLabel recording point depth
4745 
4746   Level: developer
4747 
4748 .seealso: `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
4749 @*/
4750 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
4751 {
4752   PetscFunctionBegin;
4753   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4754   PetscValidPointer(depthLabel, 2);
4755   *depthLabel = dm->depthLabel;
4756   PetscFunctionReturn(0);
4757 }
4758 
4759 /*@
4760   DMPlexGetDepth - Get the depth of the DAG representing this mesh
4761 
4762   Not Collective
4763 
4764   Input Parameter:
4765 . dm    - The DMPlex object
4766 
4767   Output Parameter:
4768 . depth - The number of strata (breadth first levels) in the DAG
4769 
4770   Level: developer
4771 
4772   Notes:
4773   This returns maximum of point depths over all points, i.e. maximum value of the label returned by DMPlexGetDepthLabel().
4774   The point depth is described more in detail in DMPlexGetDepthStratum().
4775   An empty mesh gives -1.
4776 
4777 .seealso: `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
4778 @*/
4779 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
4780 {
4781   DMLabel        label;
4782   PetscInt       d = 0;
4783 
4784   PetscFunctionBegin;
4785   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4786   PetscValidIntPointer(depth, 2);
4787   PetscCall(DMPlexGetDepthLabel(dm, &label));
4788   if (label) PetscCall(DMLabelGetNumValues(label, &d));
4789   *depth = d-1;
4790   PetscFunctionReturn(0);
4791 }
4792 
4793 /*@
4794   DMPlexGetDepthStratum - Get the bounds [start, end) for all points at a certain depth.
4795 
4796   Not Collective
4797 
4798   Input Parameters:
4799 + dm    - The DMPlex object
4800 - depth - The requested depth
4801 
4802   Output Parameters:
4803 + start - The first point at this depth
4804 - end   - One beyond the last point at this depth
4805 
4806   Notes:
4807   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
4808   often "vertices".  If the mesh is "interpolated" (see DMPlexInterpolate()), then depth stratum 1 contains the next
4809   higher dimension, e.g., "edges".
4810 
4811   Level: developer
4812 
4813 .seealso: `DMPlexGetHeightStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
4814 @*/
4815 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
4816 {
4817   DMLabel        label;
4818   PetscInt       pStart, pEnd;
4819 
4820   PetscFunctionBegin;
4821   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4822   if (start) {PetscValidIntPointer(start, 3); *start = 0;}
4823   if (end)   {PetscValidIntPointer(end,   4); *end   = 0;}
4824   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4825   if (pStart == pEnd) PetscFunctionReturn(0);
4826   if (depth < 0) {
4827     if (start) *start = pStart;
4828     if (end)   *end   = pEnd;
4829     PetscFunctionReturn(0);
4830   }
4831   PetscCall(DMPlexGetDepthLabel(dm, &label));
4832   PetscCheck(label,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
4833   PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
4834   PetscFunctionReturn(0);
4835 }
4836 
4837 /*@
4838   DMPlexGetHeightStratum - Get the bounds [start, end) for all points at a certain height.
4839 
4840   Not Collective
4841 
4842   Input Parameters:
4843 + dm     - The DMPlex object
4844 - height - The requested height
4845 
4846   Output Parameters:
4847 + start - The first point at this height
4848 - end   - One beyond the last point at this height
4849 
4850   Notes:
4851   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
4852   points, often called "cells" or "elements".  If the mesh is "interpolated" (see DMPlexInterpolate()), then height
4853   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
4854 
4855   Level: developer
4856 
4857 .seealso: `DMPlexGetDepthStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
4858 @*/
4859 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
4860 {
4861   DMLabel        label;
4862   PetscInt       depth, pStart, pEnd;
4863 
4864   PetscFunctionBegin;
4865   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4866   if (start) {PetscValidIntPointer(start, 3); *start = 0;}
4867   if (end)   {PetscValidIntPointer(end,   4); *end   = 0;}
4868   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4869   if (pStart == pEnd) PetscFunctionReturn(0);
4870   if (height < 0) {
4871     if (start) *start = pStart;
4872     if (end)   *end   = pEnd;
4873     PetscFunctionReturn(0);
4874   }
4875   PetscCall(DMPlexGetDepthLabel(dm, &label));
4876   PetscCheck(label,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
4877   PetscCall(DMLabelGetNumValues(label, &depth));
4878   PetscCall(DMLabelGetStratumBounds(label, depth-1-height, start, end));
4879   PetscFunctionReturn(0);
4880 }
4881 
4882 /*@
4883   DMPlexGetPointDepth - Get the depth of a given point
4884 
4885   Not Collective
4886 
4887   Input Parameters:
4888 + dm    - The DMPlex object
4889 - point - The point
4890 
4891   Output Parameter:
4892 . depth - The depth of the point
4893 
4894   Level: intermediate
4895 
4896 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
4897 @*/
4898 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
4899 {
4900   PetscFunctionBegin;
4901   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4902   PetscValidIntPointer(depth, 3);
4903   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
4904   PetscFunctionReturn(0);
4905 }
4906 
4907 /*@
4908   DMPlexGetPointHeight - Get the height of a given point
4909 
4910   Not Collective
4911 
4912   Input Parameters:
4913 + dm    - The DMPlex object
4914 - point - The point
4915 
4916   Output Parameter:
4917 . height - The height of the point
4918 
4919   Level: intermediate
4920 
4921 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
4922 @*/
4923 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
4924 {
4925   PetscInt       n, pDepth;
4926 
4927   PetscFunctionBegin;
4928   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4929   PetscValidIntPointer(height, 3);
4930   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
4931   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
4932   *height = n - 1 - pDepth;  /* DAG depth is n-1 */
4933   PetscFunctionReturn(0);
4934 }
4935 
4936 /*@
4937   DMPlexGetCellTypeLabel - Get the DMLabel recording the polytope type of each cell
4938 
4939   Not Collective
4940 
4941   Input Parameter:
4942 . dm - The DMPlex object
4943 
4944   Output Parameter:
4945 . celltypeLabel - The DMLabel recording cell polytope type
4946 
4947   Note: This function will trigger automatica computation of cell types. This can be disabled by calling
4948   DMCreateLabel(dm, "celltype") beforehand.
4949 
4950   Level: developer
4951 
4952 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
4953 @*/
4954 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
4955 {
4956   PetscFunctionBegin;
4957   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4958   PetscValidPointer(celltypeLabel, 2);
4959   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
4960   *celltypeLabel = dm->celltypeLabel;
4961   PetscFunctionReturn(0);
4962 }
4963 
4964 /*@
4965   DMPlexGetCellType - Get the polytope type of a given cell
4966 
4967   Not Collective
4968 
4969   Input Parameters:
4970 + dm   - The DMPlex object
4971 - cell - The cell
4972 
4973   Output Parameter:
4974 . celltype - The polytope type of the cell
4975 
4976   Level: intermediate
4977 
4978 .seealso: `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
4979 @*/
4980 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
4981 {
4982   DMLabel        label;
4983   PetscInt       ct;
4984 
4985   PetscFunctionBegin;
4986   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4987   PetscValidPointer(celltype, 3);
4988   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
4989   PetscCall(DMLabelGetValue(label, cell, &ct));
4990   PetscCheck(ct >= 0,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
4991   *celltype = (DMPolytopeType) ct;
4992   PetscFunctionReturn(0);
4993 }
4994 
4995 /*@
4996   DMPlexSetCellType - Set the polytope type of a given cell
4997 
4998   Not Collective
4999 
5000   Input Parameters:
5001 + dm   - The DMPlex object
5002 . cell - The cell
5003 - celltype - The polytope type of the cell
5004 
5005   Note: By default, cell types will be automatically computed using DMPlexComputeCellTypes() before this function
5006   is executed. This function will override the computed type. However, if automatic classification will not succeed
5007   and a user wants to manually specify all types, the classification must be disabled by calling
5008   DMCreaateLabel(dm, "celltype") before getting or setting any cell types.
5009 
5010   Level: advanced
5011 
5012 .seealso: `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5013 @*/
5014 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5015 {
5016   DMLabel        label;
5017 
5018   PetscFunctionBegin;
5019   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5020   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5021   PetscCall(DMLabelSetValue(label, cell, celltype));
5022   PetscFunctionReturn(0);
5023 }
5024 
5025 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5026 {
5027   PetscSection   section, s;
5028   Mat            m;
5029   PetscInt       maxHeight;
5030 
5031   PetscFunctionBegin;
5032   PetscCall(DMClone(dm, cdm));
5033   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5034   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5035   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5036   PetscCall(DMSetLocalSection(*cdm, section));
5037   PetscCall(PetscSectionDestroy(&section));
5038   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
5039   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
5040   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
5041   PetscCall(PetscSectionDestroy(&s));
5042   PetscCall(MatDestroy(&m));
5043 
5044   PetscCall(DMSetNumFields(*cdm, 1));
5045   PetscCall(DMCreateDS(*cdm));
5046   PetscFunctionReturn(0);
5047 }
5048 
5049 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5050 {
5051   Vec coordsLocal, cellCoordsLocal;
5052   DM  coordsDM,    cellCoordsDM;
5053 
5054   PetscFunctionBegin;
5055   *field = NULL;
5056   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5057   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5058   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5059   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5060   if (coordsLocal && coordsDM) {
5061     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5062     else                                 PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5063   }
5064   PetscFunctionReturn(0);
5065 }
5066 
5067 /*@C
5068   DMPlexGetConeSection - Return a section which describes the layout of cone data
5069 
5070   Not Collective
5071 
5072   Input Parameters:
5073 . dm        - The DMPlex object
5074 
5075   Output Parameter:
5076 . section - The PetscSection object
5077 
5078   Level: developer
5079 
5080 .seealso: `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`
5081 @*/
5082 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5083 {
5084   DM_Plex *mesh = (DM_Plex*) dm->data;
5085 
5086   PetscFunctionBegin;
5087   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5088   if (section) *section = mesh->coneSection;
5089   PetscFunctionReturn(0);
5090 }
5091 
5092 /*@C
5093   DMPlexGetSupportSection - Return a section which describes the layout of support data
5094 
5095   Not Collective
5096 
5097   Input Parameters:
5098 . dm        - The DMPlex object
5099 
5100   Output Parameter:
5101 . section - The PetscSection object
5102 
5103   Level: developer
5104 
5105 .seealso: `DMPlexGetConeSection()`
5106 @*/
5107 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5108 {
5109   DM_Plex *mesh = (DM_Plex*) dm->data;
5110 
5111   PetscFunctionBegin;
5112   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5113   if (section) *section = mesh->supportSection;
5114   PetscFunctionReturn(0);
5115 }
5116 
5117 /*@C
5118   DMPlexGetCones - Return cone data
5119 
5120   Not Collective
5121 
5122   Input Parameters:
5123 . dm        - The DMPlex object
5124 
5125   Output Parameter:
5126 . cones - The cone for each point
5127 
5128   Level: developer
5129 
5130 .seealso: `DMPlexGetConeSection()`
5131 @*/
5132 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5133 {
5134   DM_Plex *mesh = (DM_Plex*) dm->data;
5135 
5136   PetscFunctionBegin;
5137   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5138   if (cones) *cones = mesh->cones;
5139   PetscFunctionReturn(0);
5140 }
5141 
5142 /*@C
5143   DMPlexGetConeOrientations - Return cone orientation data
5144 
5145   Not Collective
5146 
5147   Input Parameters:
5148 . dm        - The DMPlex object
5149 
5150   Output Parameter:
5151 . coneOrientations - The array of cone orientations for all points
5152 
5153   Level: developer
5154 
5155   Notes:
5156   The PetscSection returned by DMPlexGetConeSection() partitions coneOrientations into cone orientations of particular points as returned by DMPlexGetConeOrientation().
5157 
5158   The meaning of coneOrientations values is detailed in DMPlexGetConeOrientation().
5159 
5160 .seealso: `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`
5161 @*/
5162 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5163 {
5164   DM_Plex *mesh = (DM_Plex*) dm->data;
5165 
5166   PetscFunctionBegin;
5167   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5168   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5169   PetscFunctionReturn(0);
5170 }
5171 
5172 /******************************** FEM Support **********************************/
5173 
5174 /*
5175  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5176  representing a line in the section.
5177 */
5178 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(PetscSection section,PetscInt field,PetscInt line,PetscBool vertexchart,PetscInt *Nc,PetscInt *k)
5179 {
5180   PetscFunctionBeginHot;
5181   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5182   if (line < 0) {
5183     *k = 0;
5184     *Nc = 0;
5185   } else if (vertexchart) {            /* If we only have a vertex chart, we must have degree k=1 */
5186     *k = 1;
5187   } else {                      /* Assume the full interpolated mesh is in the chart; lines in particular */
5188     /* An order k SEM disc has k-1 dofs on an edge */
5189     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5190     *k = *k / *Nc + 1;
5191   }
5192   PetscFunctionReturn(0);
5193 }
5194 
5195 /*@
5196 
5197   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5198   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5199   section provided (or the section of the DM).
5200 
5201   Input Parameters:
5202 + dm      - The DM
5203 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or PETSC_DETERMINE
5204 - section - The PetscSection to reorder, or NULL for the default section
5205 
5206   Note: The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5207   degree of the basis.
5208 
5209   Example:
5210   A typical interpolated single-quad mesh might order points as
5211 .vb
5212   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5213 
5214   v4 -- e6 -- v3
5215   |           |
5216   e7    c0    e8
5217   |           |
5218   v1 -- e5 -- v2
5219 .ve
5220 
5221   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5222   dofs in the order of points, e.g.,
5223 .vb
5224     c0 -> [0,1,2,3]
5225     v1 -> [4]
5226     ...
5227     e5 -> [8, 9]
5228 .ve
5229 
5230   which corresponds to the dofs
5231 .vb
5232     6   10  11  7
5233     13  2   3   15
5234     12  0   1   14
5235     4   8   9   5
5236 .ve
5237 
5238   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5239 .vb
5240   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5241 .ve
5242 
5243   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5244 .vb
5245    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5246 .ve
5247 
5248   Level: developer
5249 
5250 .seealso: `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5251 @*/
5252 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5253 {
5254   DMLabel        label;
5255   PetscInt       dim, depth = -1, eStart = -1, Nf;
5256   PetscBool      vertexchart;
5257 
5258   PetscFunctionBegin;
5259   PetscCall(DMGetDimension(dm, &dim));
5260   if (dim < 1) PetscFunctionReturn(0);
5261   if (point < 0) {
5262     PetscInt sStart,sEnd;
5263 
5264     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5265     point = sEnd-sStart ? sStart : point;
5266   }
5267   PetscCall(DMPlexGetDepthLabel(dm, &label));
5268   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5269   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5270   if (depth == 1) {eStart = point;}
5271   else if  (depth == dim) {
5272     const PetscInt *cone;
5273 
5274     PetscCall(DMPlexGetCone(dm, point, &cone));
5275     if (dim == 2) eStart = cone[0];
5276     else if (dim == 3) {
5277       const PetscInt *cone2;
5278       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5279       eStart = cone2[0];
5280     } 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);
5281   } 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);
5282   {                             /* Determine whether the chart covers all points or just vertices. */
5283     PetscInt pStart,pEnd,cStart,cEnd;
5284     PetscCall(DMPlexGetDepthStratum(dm,0,&pStart,&pEnd));
5285     PetscCall(PetscSectionGetChart(section,&cStart,&cEnd));
5286     if (pStart == cStart && pEnd == cEnd) vertexchart = PETSC_TRUE;      /* Only vertices are in the chart */
5287     else if (cStart <= point && point < cEnd) vertexchart = PETSC_FALSE; /* Some interpolated points exist in the chart */
5288     else vertexchart = PETSC_TRUE;                                       /* Some interpolated points are not in chart; assume dofs only at cells and vertices */
5289   }
5290   PetscCall(PetscSectionGetNumFields(section, &Nf));
5291   for (PetscInt d=1; d<=dim; d++) {
5292     PetscInt k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5293     PetscInt *perm;
5294 
5295     for (f = 0; f < Nf; ++f) {
5296       PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5297       size += PetscPowInt(k+1, d)*Nc;
5298     }
5299     PetscCall(PetscMalloc1(size, &perm));
5300     for (f = 0; f < Nf; ++f) {
5301       switch (d) {
5302       case 1:
5303         PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5304         /*
5305          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5306          We want              [ vtx0; edge of length k-1; vtx1 ]
5307          */
5308         for (c=0; c<Nc; c++,offset++) perm[offset] = (k-1)*Nc + c + foffset;
5309         for (i=0; i<k-1; i++) for (c=0; c<Nc; c++,offset++) perm[offset] = i*Nc + c + foffset;
5310         for (c=0; c<Nc; c++,offset++) perm[offset] = k*Nc + c + foffset;
5311         foffset = offset;
5312         break;
5313       case 2:
5314         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5315         PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5316         /* The SEM order is
5317 
5318          v_lb, {e_b}, v_rb,
5319          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5320          v_lt, reverse {e_t}, v_rt
5321          */
5322         {
5323           const PetscInt of   = 0;
5324           const PetscInt oeb  = of   + PetscSqr(k-1);
5325           const PetscInt oer  = oeb  + (k-1);
5326           const PetscInt oet  = oer  + (k-1);
5327           const PetscInt oel  = oet  + (k-1);
5328           const PetscInt ovlb = oel  + (k-1);
5329           const PetscInt ovrb = ovlb + 1;
5330           const PetscInt ovrt = ovrb + 1;
5331           const PetscInt ovlt = ovrt + 1;
5332           PetscInt       o;
5333 
5334           /* bottom */
5335           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb*Nc + c + foffset;
5336           for (o = oeb; o < oer; ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5337           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb*Nc + c + foffset;
5338           /* middle */
5339           for (i = 0; i < k-1; ++i) {
5340             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel+(k-2)-i)*Nc + c + foffset;
5341             for (o = of+(k-1)*i; o < of+(k-1)*(i+1); ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5342             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer+i)*Nc + c + foffset;
5343           }
5344           /* top */
5345           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt*Nc + c + foffset;
5346           for (o = oel-1; o >= oet; --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5347           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt*Nc + c + foffset;
5348           foffset = offset;
5349         }
5350         break;
5351       case 3:
5352         /* The original hex closure is
5353 
5354          {c,
5355          f_b, f_t, f_f, f_b, f_r, f_l,
5356          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5357          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5358          */
5359         PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5360         /* The SEM order is
5361          Bottom Slice
5362          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5363          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5364          v_blb, {e_bb}, v_brb,
5365 
5366          Middle Slice (j)
5367          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5368          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5369          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5370 
5371          Top Slice
5372          v_tlf, {e_tf}, v_trf,
5373          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5374          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5375          */
5376         {
5377           const PetscInt oc    = 0;
5378           const PetscInt ofb   = oc    + PetscSqr(k-1)*(k-1);
5379           const PetscInt oft   = ofb   + PetscSqr(k-1);
5380           const PetscInt off   = oft   + PetscSqr(k-1);
5381           const PetscInt ofk   = off   + PetscSqr(k-1);
5382           const PetscInt ofr   = ofk   + PetscSqr(k-1);
5383           const PetscInt ofl   = ofr   + PetscSqr(k-1);
5384           const PetscInt oebl  = ofl   + PetscSqr(k-1);
5385           const PetscInt oebb  = oebl  + (k-1);
5386           const PetscInt oebr  = oebb  + (k-1);
5387           const PetscInt oebf  = oebr  + (k-1);
5388           const PetscInt oetf  = oebf  + (k-1);
5389           const PetscInt oetr  = oetf  + (k-1);
5390           const PetscInt oetb  = oetr  + (k-1);
5391           const PetscInt oetl  = oetb  + (k-1);
5392           const PetscInt oerf  = oetl  + (k-1);
5393           const PetscInt oelf  = oerf  + (k-1);
5394           const PetscInt oelb  = oelf  + (k-1);
5395           const PetscInt oerb  = oelb  + (k-1);
5396           const PetscInt ovblf = oerb  + (k-1);
5397           const PetscInt ovblb = ovblf + 1;
5398           const PetscInt ovbrb = ovblb + 1;
5399           const PetscInt ovbrf = ovbrb + 1;
5400           const PetscInt ovtlf = ovbrf + 1;
5401           const PetscInt ovtrf = ovtlf + 1;
5402           const PetscInt ovtrb = ovtrf + 1;
5403           const PetscInt ovtlb = ovtrb + 1;
5404           PetscInt       o, n;
5405 
5406           /* Bottom Slice */
5407           /*   bottom */
5408           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf*Nc + c + foffset;
5409           for (o = oetf-1; o >= oebf; --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5410           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf*Nc + c + foffset;
5411           /*   middle */
5412           for (i = 0; i < k-1; ++i) {
5413             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl+i)*Nc + c + foffset;
5414             for (n = 0; n < k-1; ++n) {o = ofb+n*(k-1)+i; for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;}
5415             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr+(k-2)-i)*Nc + c + foffset;
5416           }
5417           /*   top */
5418           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb*Nc + c + foffset;
5419           for (o = oebb; o < oebr; ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5420           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb*Nc + c + foffset;
5421 
5422           /* Middle Slice */
5423           for (j = 0; j < k-1; ++j) {
5424             /*   bottom */
5425             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf+(k-2)-j)*Nc + c + foffset;
5426             for (o = off+j*(k-1); o < off+(j+1)*(k-1); ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5427             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf+j)*Nc + c + foffset;
5428             /*   middle */
5429             for (i = 0; i < k-1; ++i) {
5430               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl+i*(k-1)+j)*Nc + c + foffset;
5431               for (n = 0; n < k-1; ++n) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc+(j*(k-1)+i)*(k-1)+n)*Nc + c + foffset;
5432               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr+j*(k-1)+i)*Nc + c + foffset;
5433             }
5434             /*   top */
5435             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb+j)*Nc + c + foffset;
5436             for (o = ofk+j*(k-1)+(k-2); o >= ofk+j*(k-1); --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5437             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb+(k-2)-j)*Nc + c + foffset;
5438           }
5439 
5440           /* Top Slice */
5441           /*   bottom */
5442           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf*Nc + c + foffset;
5443           for (o = oetf; o < oetr; ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5444           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf*Nc + c + foffset;
5445           /*   middle */
5446           for (i = 0; i < k-1; ++i) {
5447             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl+(k-2)-i)*Nc + c + foffset;
5448             for (n = 0; n < k-1; ++n) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft+i*(k-1)+n)*Nc + c + foffset;
5449             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr+i)*Nc + c + foffset;
5450           }
5451           /*   top */
5452           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb*Nc + c + foffset;
5453           for (o = oetl-1; o >= oetb; --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5454           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb*Nc + c + foffset;
5455 
5456           foffset = offset;
5457         }
5458         break;
5459       default: SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
5460       }
5461     }
5462     PetscCheck(offset == size,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
5463     /* Check permutation */
5464     {
5465       PetscInt *check;
5466 
5467       PetscCall(PetscMalloc1(size, &check));
5468       for (i = 0; i < size; ++i) {
5469         check[i] = -1;
5470         PetscCheck(perm[i] >= 0 && perm[i] < size,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
5471       }
5472       for (i = 0; i < size; ++i) check[perm[i]] = i;
5473       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
5474       PetscCall(PetscFree(check));
5475     }
5476     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject) dm, d, size, PETSC_OWN_POINTER, perm));
5477     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
5478       PetscInt *loc_perm;
5479       PetscCall(PetscMalloc1(size*2, &loc_perm));
5480       for (PetscInt i=0; i<size; i++) {
5481         loc_perm[i] = perm[i];
5482         loc_perm[size+i] = size + perm[i];
5483       }
5484       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject) dm, d, size*2, PETSC_OWN_POINTER, loc_perm));
5485     }
5486   }
5487   PetscFunctionReturn(0);
5488 }
5489 
5490 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
5491 {
5492   PetscDS        prob;
5493   PetscInt       depth, Nf, h;
5494   DMLabel        label;
5495 
5496   PetscFunctionBeginHot;
5497   PetscCall(DMGetDS(dm, &prob));
5498   Nf      = prob->Nf;
5499   label   = dm->depthLabel;
5500   *dspace = NULL;
5501   if (field < Nf) {
5502     PetscObject disc = prob->disc[field];
5503 
5504     if (disc->classid == PETSCFE_CLASSID) {
5505       PetscDualSpace dsp;
5506 
5507       PetscCall(PetscFEGetDualSpace((PetscFE)disc,&dsp));
5508       PetscCall(DMLabelGetNumValues(label,&depth));
5509       PetscCall(DMLabelGetValue(label,point,&h));
5510       h    = depth - 1 - h;
5511       if (h) {
5512         PetscCall(PetscDualSpaceGetHeightSubspace(dsp,h,dspace));
5513       } else {
5514         *dspace = dsp;
5515       }
5516     }
5517   }
5518   PetscFunctionReturn(0);
5519 }
5520 
5521 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5522 {
5523   PetscScalar    *array;
5524   const PetscScalar *vArray;
5525   const PetscInt *cone, *coneO;
5526   PetscInt        pStart, pEnd, p, numPoints, size = 0, offset = 0;
5527 
5528   PetscFunctionBeginHot;
5529   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5530   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
5531   PetscCall(DMPlexGetCone(dm, point, &cone));
5532   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
5533   if (!values || !*values) {
5534     if ((point >= pStart) && (point < pEnd)) {
5535       PetscInt dof;
5536 
5537       PetscCall(PetscSectionGetDof(section, point, &dof));
5538       size += dof;
5539     }
5540     for (p = 0; p < numPoints; ++p) {
5541       const PetscInt cp = cone[p];
5542       PetscInt       dof;
5543 
5544       if ((cp < pStart) || (cp >= pEnd)) continue;
5545       PetscCall(PetscSectionGetDof(section, cp, &dof));
5546       size += dof;
5547     }
5548     if (!values) {
5549       if (csize) *csize = size;
5550       PetscFunctionReturn(0);
5551     }
5552     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
5553   } else {
5554     array = *values;
5555   }
5556   size = 0;
5557   PetscCall(VecGetArrayRead(v, &vArray));
5558   if ((point >= pStart) && (point < pEnd)) {
5559     PetscInt     dof, off, d;
5560     const PetscScalar *varr;
5561 
5562     PetscCall(PetscSectionGetDof(section, point, &dof));
5563     PetscCall(PetscSectionGetOffset(section, point, &off));
5564     varr = &vArray[off];
5565     for (d = 0; d < dof; ++d, ++offset) {
5566       array[offset] = varr[d];
5567     }
5568     size += dof;
5569   }
5570   for (p = 0; p < numPoints; ++p) {
5571     const PetscInt cp = cone[p];
5572     PetscInt       o  = coneO[p];
5573     PetscInt       dof, off, d;
5574     const PetscScalar *varr;
5575 
5576     if ((cp < pStart) || (cp >= pEnd)) continue;
5577     PetscCall(PetscSectionGetDof(section, cp, &dof));
5578     PetscCall(PetscSectionGetOffset(section, cp, &off));
5579     varr = &vArray[off];
5580     if (o >= 0) {
5581       for (d = 0; d < dof; ++d, ++offset) {
5582         array[offset] = varr[d];
5583       }
5584     } else {
5585       for (d = dof-1; d >= 0; --d, ++offset) {
5586         array[offset] = varr[d];
5587       }
5588     }
5589     size += dof;
5590   }
5591   PetscCall(VecRestoreArrayRead(v, &vArray));
5592   if (!*values) {
5593     if (csize) *csize = size;
5594     *values = array;
5595   } else {
5596     PetscCheck(size <= *csize,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5597     *csize = size;
5598   }
5599   PetscFunctionReturn(0);
5600 }
5601 
5602 /* Compress out points not in the section */
5603 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
5604 {
5605   const PetscInt np = *numPoints;
5606   PetscInt       pStart, pEnd, p, q;
5607 
5608   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5609   for (p = 0, q = 0; p < np; ++p) {
5610     const PetscInt r = points[p*2];
5611     if ((r >= pStart) && (r < pEnd)) {
5612       points[q*2]   = r;
5613       points[q*2+1] = points[p*2+1];
5614       ++q;
5615     }
5616   }
5617   *numPoints = q;
5618   return 0;
5619 }
5620 
5621 /* Compressed closure does not apply closure permutation */
5622 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5623 {
5624   const PetscInt *cla = NULL;
5625   PetscInt       np, *pts = NULL;
5626 
5627   PetscFunctionBeginHot;
5628   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject) dm, clSec, clPoints));
5629   if (*clPoints) {
5630     PetscInt dof, off;
5631 
5632     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
5633     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
5634     PetscCall(ISGetIndices(*clPoints, &cla));
5635     np   = dof/2;
5636     pts  = (PetscInt *) &cla[off];
5637   } else {
5638     PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &np, &pts));
5639     PetscCall(CompressPoints_Private(section, &np, pts));
5640   }
5641   *numPoints = np;
5642   *points    = pts;
5643   *clp       = cla;
5644   PetscFunctionReturn(0);
5645 }
5646 
5647 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5648 {
5649   PetscFunctionBeginHot;
5650   if (!*clPoints) {
5651     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
5652   } else {
5653     PetscCall(ISRestoreIndices(*clPoints, clp));
5654   }
5655   *numPoints = 0;
5656   *points    = NULL;
5657   *clSec     = NULL;
5658   *clPoints  = NULL;
5659   *clp       = NULL;
5660   PetscFunctionReturn(0);
5661 }
5662 
5663 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
5664 {
5665   PetscInt          offset = 0, p;
5666   const PetscInt    **perms = NULL;
5667   const PetscScalar **flips = NULL;
5668 
5669   PetscFunctionBeginHot;
5670   *size = 0;
5671   PetscCall(PetscSectionGetPointSyms(section,numPoints,points,&perms,&flips));
5672   for (p = 0; p < numPoints; p++) {
5673     const PetscInt    point = points[2*p];
5674     const PetscInt    *perm = perms ? perms[p] : NULL;
5675     const PetscScalar *flip = flips ? flips[p] : NULL;
5676     PetscInt          dof, off, d;
5677     const PetscScalar *varr;
5678 
5679     PetscCall(PetscSectionGetDof(section, point, &dof));
5680     PetscCall(PetscSectionGetOffset(section, point, &off));
5681     varr = &vArray[off];
5682     if (clperm) {
5683       if (perm) {
5684         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]]  = varr[d];
5685       } else {
5686         for (d = 0; d < dof; d++) array[clperm[offset +      d ]]  = varr[d];
5687       }
5688       if (flip) {
5689         for (d = 0; d < dof; d++) array[clperm[offset +      d ]] *= flip[d];
5690       }
5691     } else {
5692       if (perm) {
5693         for (d = 0; d < dof; d++) array[offset + perm[d]]  = varr[d];
5694       } else {
5695         for (d = 0; d < dof; d++) array[offset +      d ]  = varr[d];
5696       }
5697       if (flip) {
5698         for (d = 0; d < dof; d++) array[offset +      d ] *= flip[d];
5699       }
5700     }
5701     offset += dof;
5702   }
5703   PetscCall(PetscSectionRestorePointSyms(section,numPoints,points,&perms,&flips));
5704   *size = offset;
5705   PetscFunctionReturn(0);
5706 }
5707 
5708 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[])
5709 {
5710   PetscInt          offset = 0, f;
5711 
5712   PetscFunctionBeginHot;
5713   *size = 0;
5714   for (f = 0; f < numFields; ++f) {
5715     PetscInt          p;
5716     const PetscInt    **perms = NULL;
5717     const PetscScalar **flips = NULL;
5718 
5719     PetscCall(PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips));
5720     for (p = 0; p < numPoints; p++) {
5721       const PetscInt    point = points[2*p];
5722       PetscInt          fdof, foff, b;
5723       const PetscScalar *varr;
5724       const PetscInt    *perm = perms ? perms[p] : NULL;
5725       const PetscScalar *flip = flips ? flips[p] : NULL;
5726 
5727       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
5728       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
5729       varr = &vArray[foff];
5730       if (clperm) {
5731         if (perm) {for (b = 0; b < fdof; b++) {array[clperm[offset + perm[b]]]  = varr[b];}}
5732         else      {for (b = 0; b < fdof; b++) {array[clperm[offset +      b ]]  = varr[b];}}
5733         if (flip) {for (b = 0; b < fdof; b++) {array[clperm[offset +      b ]] *= flip[b];}}
5734       } else {
5735         if (perm) {for (b = 0; b < fdof; b++) {array[offset + perm[b]]  = varr[b];}}
5736         else      {for (b = 0; b < fdof; b++) {array[offset +      b ]  = varr[b];}}
5737         if (flip) {for (b = 0; b < fdof; b++) {array[offset +      b ] *= flip[b];}}
5738       }
5739       offset += fdof;
5740     }
5741     PetscCall(PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips));
5742   }
5743   *size = offset;
5744   PetscFunctionReturn(0);
5745 }
5746 
5747 /*@C
5748   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
5749 
5750   Not collective
5751 
5752   Input Parameters:
5753 + dm - The DM
5754 . section - The section describing the layout in v, or NULL to use the default section
5755 . v - The local vector
5756 - point - The point in the DM
5757 
5758   Input/Output Parameters:
5759 + csize  - The size of the input values array, or NULL; on output the number of values in the closure
5760 - values - An array to use for the values, or NULL to have it allocated automatically;
5761            if the user provided NULL, it is a borrowed array and should not be freed
5762 
5763 $ Note that DMPlexVecGetClosure/DMPlexVecRestoreClosure only allocates the values array if it set to NULL in the
5764 $ calling function. This is because DMPlexVecGetClosure() is typically called in the inner loop of a Vec or Mat
5765 $ assembly function, and a user may already have allocated storage for this operation.
5766 $
5767 $ A typical use could be
5768 $
5769 $  values = NULL;
5770 $  PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5771 $  for (cl = 0; cl < clSize; ++cl) {
5772 $    <Compute on closure>
5773 $  }
5774 $  PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
5775 $
5776 $ or
5777 $
5778 $  PetscMalloc1(clMaxSize, &values);
5779 $  for (p = pStart; p < pEnd; ++p) {
5780 $    clSize = clMaxSize;
5781 $    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5782 $    for (cl = 0; cl < clSize; ++cl) {
5783 $      <Compute on closure>
5784 $    }
5785 $  }
5786 $  PetscFree(values);
5787 
5788   Fortran Notes:
5789   Since it returns an array, this routine is only available in Fortran 90, and you must
5790   include petsc.h90 in your code.
5791 
5792   The csize argument is not present in the Fortran 90 binding since it is internal to the array.
5793 
5794   Level: intermediate
5795 
5796 .seealso `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
5797 @*/
5798 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5799 {
5800   PetscSection       clSection;
5801   IS                 clPoints;
5802   PetscInt          *points = NULL;
5803   const PetscInt    *clp, *perm;
5804   PetscInt           depth, numFields, numPoints, asize;
5805 
5806   PetscFunctionBeginHot;
5807   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5808   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5809   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5810   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5811   PetscCall(DMPlexGetDepth(dm, &depth));
5812   PetscCall(PetscSectionGetNumFields(section, &numFields));
5813   if (depth == 1 && numFields < 2) {
5814     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
5815     PetscFunctionReturn(0);
5816   }
5817   /* Get points */
5818   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5819   /* Get sizes */
5820   asize = 0;
5821   for (PetscInt p = 0; p < numPoints*2; p += 2) {
5822     PetscInt dof;
5823     PetscCall(PetscSectionGetDof(section, points[p], &dof));
5824     asize += dof;
5825   }
5826   if (values) {
5827     const PetscScalar *vArray;
5828     PetscInt          size;
5829 
5830     if (*values) {
5831       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);
5832     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
5833     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, asize, &perm));
5834     PetscCall(VecGetArrayRead(v, &vArray));
5835     /* Get values */
5836     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
5837     else               PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
5838     PetscCheck(asize == size,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
5839     /* Cleanup array */
5840     PetscCall(VecRestoreArrayRead(v, &vArray));
5841   }
5842   if (csize) *csize = asize;
5843   /* Cleanup points */
5844   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5845   PetscFunctionReturn(0);
5846 }
5847 
5848 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
5849 {
5850   DMLabel            depthLabel;
5851   PetscSection       clSection;
5852   IS                 clPoints;
5853   PetscScalar       *array;
5854   const PetscScalar *vArray;
5855   PetscInt          *points = NULL;
5856   const PetscInt    *clp, *perm = NULL;
5857   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
5858 
5859   PetscFunctionBeginHot;
5860   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5861   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5862   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5863   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5864   PetscCall(DMPlexGetDepth(dm, &mdepth));
5865   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5866   PetscCall(PetscSectionGetNumFields(section, &numFields));
5867   if (mdepth == 1 && numFields < 2) {
5868     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
5869     PetscFunctionReturn(0);
5870   }
5871   /* Get points */
5872   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5873   for (clsize=0,p=0; p<Np; p++) {
5874     PetscInt dof;
5875     PetscCall(PetscSectionGetDof(section, points[2*p], &dof));
5876     clsize += dof;
5877   }
5878   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, clsize, &perm));
5879   /* Filter points */
5880   for (p = 0; p < numPoints*2; p += 2) {
5881     PetscInt dep;
5882 
5883     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
5884     if (dep != depth) continue;
5885     points[Np*2+0] = points[p];
5886     points[Np*2+1] = points[p+1];
5887     ++Np;
5888   }
5889   /* Get array */
5890   if (!values || !*values) {
5891     PetscInt asize = 0, dof;
5892 
5893     for (p = 0; p < Np*2; p += 2) {
5894       PetscCall(PetscSectionGetDof(section, points[p], &dof));
5895       asize += dof;
5896     }
5897     if (!values) {
5898       PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5899       if (csize) *csize = asize;
5900       PetscFunctionReturn(0);
5901     }
5902     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
5903   } else {
5904     array = *values;
5905   }
5906   PetscCall(VecGetArrayRead(v, &vArray));
5907   /* Get values */
5908   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
5909   else               PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
5910   /* Cleanup points */
5911   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5912   /* Cleanup array */
5913   PetscCall(VecRestoreArrayRead(v, &vArray));
5914   if (!*values) {
5915     if (csize) *csize = size;
5916     *values = array;
5917   } else {
5918     PetscCheck(size <= *csize,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5919     *csize = size;
5920   }
5921   PetscFunctionReturn(0);
5922 }
5923 
5924 /*@C
5925   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
5926 
5927   Not collective
5928 
5929   Input Parameters:
5930 + dm - The DM
5931 . section - The section describing the layout in v, or NULL to use the default section
5932 . v - The local vector
5933 . point - The point in the DM
5934 . csize - The number of values in the closure, or NULL
5935 - values - The array of values, which is a borrowed array and should not be freed
5936 
5937   Note that the array values are discarded and not copied back into v. In order to copy values back to v, use DMPlexVecSetClosure()
5938 
5939   Fortran Notes:
5940   Since it returns an array, this routine is only available in Fortran 90, and you must
5941   include petsc.h90 in your code.
5942 
5943   The csize argument is not present in the Fortran 90 binding since it is internal to the array.
5944 
5945   Level: intermediate
5946 
5947 .seealso `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
5948 @*/
5949 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5950 {
5951   PetscInt       size = 0;
5952 
5953   PetscFunctionBegin;
5954   /* Should work without recalculating size */
5955   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void*) values));
5956   *values = NULL;
5957   PetscFunctionReturn(0);
5958 }
5959 
5960 static inline void add   (PetscScalar *x, PetscScalar y) {*x += y;}
5961 static inline void insert(PetscScalar *x, PetscScalar y) {*x  = y;}
5962 
5963 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[])
5964 {
5965   PetscInt        cdof;   /* The number of constraints on this point */
5966   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
5967   PetscScalar    *a;
5968   PetscInt        off, cind = 0, k;
5969 
5970   PetscFunctionBegin;
5971   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
5972   PetscCall(PetscSectionGetOffset(section, point, &off));
5973   a    = &array[off];
5974   if (!cdof || setBC) {
5975     if (clperm) {
5976       if (perm) {for (k = 0; k < dof; ++k) {fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));}}
5977       else      {for (k = 0; k < dof; ++k) {fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));}}
5978     } else {
5979       if (perm) {for (k = 0; k < dof; ++k) {fuse(&a[k], values[offset+perm[k]] * (flip ? flip[perm[k]] : 1.));}}
5980       else      {for (k = 0; k < dof; ++k) {fuse(&a[k], values[offset+     k ] * (flip ? flip[     k ] : 1.));}}
5981     }
5982   } else {
5983     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
5984     if (clperm) {
5985       if (perm) {for (k = 0; k < dof; ++k) {
5986           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5987           fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));
5988         }
5989       } else {
5990         for (k = 0; k < dof; ++k) {
5991           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5992           fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));
5993         }
5994       }
5995     } else {
5996       if (perm) {
5997         for (k = 0; k < dof; ++k) {
5998           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5999           fuse(&a[k], values[offset+perm[k]] * (flip ? flip[perm[k]] : 1.));
6000         }
6001       } else {
6002         for (k = 0; k < dof; ++k) {
6003           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
6004           fuse(&a[k], values[offset+     k ] * (flip ? flip[     k ] : 1.));
6005         }
6006       }
6007     }
6008   }
6009   PetscFunctionReturn(0);
6010 }
6011 
6012 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[])
6013 {
6014   PetscInt        cdof;   /* The number of constraints on this point */
6015   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6016   PetscScalar    *a;
6017   PetscInt        off, cind = 0, k;
6018 
6019   PetscFunctionBegin;
6020   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6021   PetscCall(PetscSectionGetOffset(section, point, &off));
6022   a    = &array[off];
6023   if (cdof) {
6024     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6025     if (clperm) {
6026       if (perm) {
6027         for (k = 0; k < dof; ++k) {
6028           if ((cind < cdof) && (k == cdofs[cind])) {
6029             fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));
6030             cind++;
6031           }
6032         }
6033       } else {
6034         for (k = 0; k < dof; ++k) {
6035           if ((cind < cdof) && (k == cdofs[cind])) {
6036             fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));
6037             cind++;
6038           }
6039         }
6040       }
6041     } else {
6042       if (perm) {
6043         for (k = 0; k < dof; ++k) {
6044           if ((cind < cdof) && (k == cdofs[cind])) {
6045             fuse(&a[k], values[offset+perm[k]] * (flip ? flip[perm[k]] : 1.));
6046             cind++;
6047           }
6048         }
6049       } else {
6050         for (k = 0; k < dof; ++k) {
6051           if ((cind < cdof) && (k == cdofs[cind])) {
6052             fuse(&a[k], values[offset+     k ] * (flip ? flip[     k ] : 1.));
6053             cind++;
6054           }
6055         }
6056       }
6057     }
6058   }
6059   PetscFunctionReturn(0);
6060 }
6061 
6062 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[])
6063 {
6064   PetscScalar    *a;
6065   PetscInt        fdof, foff, fcdof, foffset = *offset;
6066   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6067   PetscInt        cind = 0, b;
6068 
6069   PetscFunctionBegin;
6070   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6071   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6072   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6073   a    = &array[foff];
6074   if (!fcdof || setBC) {
6075     if (clperm) {
6076       if (perm) {for (b = 0; b < fdof; b++) {fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));}}
6077       else      {for (b = 0; b < fdof; b++) {fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));}}
6078     } else {
6079       if (perm) {for (b = 0; b < fdof; b++) {fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));}}
6080       else      {for (b = 0; b < fdof; b++) {fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));}}
6081     }
6082   } else {
6083     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6084     if (clperm) {
6085       if (perm) {
6086         for (b = 0; b < fdof; b++) {
6087           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6088           fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));
6089         }
6090       } else {
6091         for (b = 0; b < fdof; b++) {
6092           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6093           fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));
6094         }
6095       }
6096     } else {
6097       if (perm) {
6098         for (b = 0; b < fdof; b++) {
6099           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6100           fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));
6101         }
6102       } else {
6103         for (b = 0; b < fdof; b++) {
6104           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6105           fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));
6106         }
6107       }
6108     }
6109   }
6110   *offset += fdof;
6111   PetscFunctionReturn(0);
6112 }
6113 
6114 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[])
6115 {
6116   PetscScalar    *a;
6117   PetscInt        fdof, foff, fcdof, foffset = *offset;
6118   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6119   PetscInt        Nc, cind = 0, ncind = 0, b;
6120   PetscBool       ncSet, fcSet;
6121 
6122   PetscFunctionBegin;
6123   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6124   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6125   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6126   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6127   a    = &array[foff];
6128   if (fcdof) {
6129     /* We just override fcdof and fcdofs with Ncc and comps */
6130     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6131     if (clperm) {
6132       if (perm) {
6133         if (comps) {
6134           for (b = 0; b < fdof; b++) {
6135             ncSet = fcSet = PETSC_FALSE;
6136             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6137             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6138             if (ncSet && fcSet) {fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));}
6139           }
6140         } else {
6141           for (b = 0; b < fdof; b++) {
6142             if ((cind < fcdof) && (b == fcdofs[cind])) {
6143               fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));
6144               ++cind;
6145             }
6146           }
6147         }
6148       } else {
6149         if (comps) {
6150           for (b = 0; b < fdof; b++) {
6151             ncSet = fcSet = PETSC_FALSE;
6152             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6153             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6154             if (ncSet && fcSet) {fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));}
6155           }
6156         } else {
6157           for (b = 0; b < fdof; b++) {
6158             if ((cind < fcdof) && (b == fcdofs[cind])) {
6159               fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));
6160               ++cind;
6161             }
6162           }
6163         }
6164       }
6165     } else {
6166       if (perm) {
6167         if (comps) {
6168           for (b = 0; b < fdof; b++) {
6169             ncSet = fcSet = PETSC_FALSE;
6170             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6171             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6172             if (ncSet && fcSet) {fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));}
6173           }
6174         } else {
6175           for (b = 0; b < fdof; b++) {
6176             if ((cind < fcdof) && (b == fcdofs[cind])) {
6177               fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));
6178               ++cind;
6179             }
6180           }
6181         }
6182       } else {
6183         if (comps) {
6184           for (b = 0; b < fdof; b++) {
6185             ncSet = fcSet = PETSC_FALSE;
6186             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6187             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6188             if (ncSet && fcSet) {fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));}
6189           }
6190         } else {
6191           for (b = 0; b < fdof; b++) {
6192             if ((cind < fcdof) && (b == fcdofs[cind])) {
6193               fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));
6194               ++cind;
6195             }
6196           }
6197         }
6198       }
6199     }
6200   }
6201   *offset += fdof;
6202   PetscFunctionReturn(0);
6203 }
6204 
6205 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6206 {
6207   PetscScalar    *array;
6208   const PetscInt *cone, *coneO;
6209   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6210 
6211   PetscFunctionBeginHot;
6212   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6213   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6214   PetscCall(DMPlexGetCone(dm, point, &cone));
6215   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6216   PetscCall(VecGetArray(v, &array));
6217   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6218     const PetscInt cp = !p ? point : cone[p-1];
6219     const PetscInt o  = !p ? 0     : coneO[p-1];
6220 
6221     if ((cp < pStart) || (cp >= pEnd)) {dof = 0; continue;}
6222     PetscCall(PetscSectionGetDof(section, cp, &dof));
6223     /* ADD_VALUES */
6224     {
6225       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6226       PetscScalar    *a;
6227       PetscInt        cdof, coff, cind = 0, k;
6228 
6229       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6230       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6231       a    = &array[coff];
6232       if (!cdof) {
6233         if (o >= 0) {
6234           for (k = 0; k < dof; ++k) {
6235             a[k] += values[off+k];
6236           }
6237         } else {
6238           for (k = 0; k < dof; ++k) {
6239             a[k] += values[off+dof-k-1];
6240           }
6241         }
6242       } else {
6243         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6244         if (o >= 0) {
6245           for (k = 0; k < dof; ++k) {
6246             if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
6247             a[k] += values[off+k];
6248           }
6249         } else {
6250           for (k = 0; k < dof; ++k) {
6251             if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
6252             a[k] += values[off+dof-k-1];
6253           }
6254         }
6255       }
6256     }
6257   }
6258   PetscCall(VecRestoreArray(v, &array));
6259   PetscFunctionReturn(0);
6260 }
6261 
6262 /*@C
6263   DMPlexVecSetClosure - Set an array of the values on the closure of 'point'
6264 
6265   Not collective
6266 
6267   Input Parameters:
6268 + dm - The DM
6269 . section - The section describing the layout in v, or NULL to use the default section
6270 . v - The local vector
6271 . point - The point in the DM
6272 . values - The array of values
6273 - mode - The insert mode. One of INSERT_ALL_VALUES, ADD_ALL_VALUES, INSERT_VALUES, ADD_VALUES, INSERT_BC_VALUES, and ADD_BC_VALUES,
6274          where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions.
6275 
6276   Fortran Notes:
6277   This routine is only available in Fortran 90, and you must include petsc.h90 in your code.
6278 
6279   Level: intermediate
6280 
6281 .seealso `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6282 @*/
6283 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6284 {
6285   PetscSection    clSection;
6286   IS              clPoints;
6287   PetscScalar    *array;
6288   PetscInt       *points = NULL;
6289   const PetscInt *clp, *clperm = NULL;
6290   PetscInt        depth, numFields, numPoints, p, clsize;
6291 
6292   PetscFunctionBeginHot;
6293   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6294   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6295   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6296   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6297   PetscCall(DMPlexGetDepth(dm, &depth));
6298   PetscCall(PetscSectionGetNumFields(section, &numFields));
6299   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6300     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6301     PetscFunctionReturn(0);
6302   }
6303   /* Get points */
6304   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6305   for (clsize=0,p=0; p<numPoints; p++) {
6306     PetscInt dof;
6307     PetscCall(PetscSectionGetDof(section, points[2*p], &dof));
6308     clsize += dof;
6309   }
6310   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, clsize, &clperm));
6311   /* Get array */
6312   PetscCall(VecGetArray(v, &array));
6313   /* Get values */
6314   if (numFields > 0) {
6315     PetscInt offset = 0, f;
6316     for (f = 0; f < numFields; ++f) {
6317       const PetscInt    **perms = NULL;
6318       const PetscScalar **flips = NULL;
6319 
6320       PetscCall(PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6321       switch (mode) {
6322       case INSERT_VALUES:
6323         for (p = 0; p < numPoints; p++) {
6324           const PetscInt    point = points[2*p];
6325           const PetscInt    *perm = perms ? perms[p] : NULL;
6326           const PetscScalar *flip = flips ? flips[p] : NULL;
6327           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array);
6328         } break;
6329       case INSERT_ALL_VALUES:
6330         for (p = 0; p < numPoints; p++) {
6331           const PetscInt    point = points[2*p];
6332           const PetscInt    *perm = perms ? perms[p] : NULL;
6333           const PetscScalar *flip = flips ? flips[p] : NULL;
6334           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array);
6335         } break;
6336       case INSERT_BC_VALUES:
6337         for (p = 0; p < numPoints; p++) {
6338           const PetscInt    point = points[2*p];
6339           const PetscInt    *perm = perms ? perms[p] : NULL;
6340           const PetscScalar *flip = flips ? flips[p] : NULL;
6341           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array);
6342         } break;
6343       case ADD_VALUES:
6344         for (p = 0; p < numPoints; p++) {
6345           const PetscInt    point = points[2*p];
6346           const PetscInt    *perm = perms ? perms[p] : NULL;
6347           const PetscScalar *flip = flips ? flips[p] : NULL;
6348           updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array);
6349         } break;
6350       case ADD_ALL_VALUES:
6351         for (p = 0; p < numPoints; p++) {
6352           const PetscInt    point = points[2*p];
6353           const PetscInt    *perm = perms ? perms[p] : NULL;
6354           const PetscScalar *flip = flips ? flips[p] : NULL;
6355           updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array);
6356         } break;
6357       case ADD_BC_VALUES:
6358         for (p = 0; p < numPoints; p++) {
6359           const PetscInt    point = points[2*p];
6360           const PetscInt    *perm = perms ? perms[p] : NULL;
6361           const PetscScalar *flip = flips ? flips[p] : NULL;
6362           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array);
6363         } break;
6364       default:
6365         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6366       }
6367       PetscCall(PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6368     }
6369   } else {
6370     PetscInt dof, off;
6371     const PetscInt    **perms = NULL;
6372     const PetscScalar **flips = NULL;
6373 
6374     PetscCall(PetscSectionGetPointSyms(section,numPoints,points,&perms,&flips));
6375     switch (mode) {
6376     case INSERT_VALUES:
6377       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6378         const PetscInt    point = points[2*p];
6379         const PetscInt    *perm = perms ? perms[p] : NULL;
6380         const PetscScalar *flip = flips ? flips[p] : NULL;
6381         PetscCall(PetscSectionGetDof(section, point, &dof));
6382         updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array);
6383       } break;
6384     case INSERT_ALL_VALUES:
6385       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6386         const PetscInt    point = points[2*p];
6387         const PetscInt    *perm = perms ? perms[p] : NULL;
6388         const PetscScalar *flip = flips ? flips[p] : NULL;
6389         PetscCall(PetscSectionGetDof(section, point, &dof));
6390         updatePoint_private(section, point, dof, insert, PETSC_TRUE,  perm, flip, clperm, values, off, array);
6391       } break;
6392     case INSERT_BC_VALUES:
6393       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6394         const PetscInt    point = points[2*p];
6395         const PetscInt    *perm = perms ? perms[p] : NULL;
6396         const PetscScalar *flip = flips ? flips[p] : NULL;
6397         PetscCall(PetscSectionGetDof(section, point, &dof));
6398         updatePointBC_private(section, point, dof, insert,  perm, flip, clperm, values, off, array);
6399       } break;
6400     case ADD_VALUES:
6401       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6402         const PetscInt    point = points[2*p];
6403         const PetscInt    *perm = perms ? perms[p] : NULL;
6404         const PetscScalar *flip = flips ? flips[p] : NULL;
6405         PetscCall(PetscSectionGetDof(section, point, &dof));
6406         updatePoint_private(section, point, dof, add,    PETSC_FALSE, perm, flip, clperm, values, off, array);
6407       } break;
6408     case ADD_ALL_VALUES:
6409       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6410         const PetscInt    point = points[2*p];
6411         const PetscInt    *perm = perms ? perms[p] : NULL;
6412         const PetscScalar *flip = flips ? flips[p] : NULL;
6413         PetscCall(PetscSectionGetDof(section, point, &dof));
6414         updatePoint_private(section, point, dof, add,    PETSC_TRUE,  perm, flip, clperm, values, off, array);
6415       } break;
6416     case ADD_BC_VALUES:
6417       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6418         const PetscInt    point = points[2*p];
6419         const PetscInt    *perm = perms ? perms[p] : NULL;
6420         const PetscScalar *flip = flips ? flips[p] : NULL;
6421         PetscCall(PetscSectionGetDof(section, point, &dof));
6422         updatePointBC_private(section, point, dof, add,  perm, flip, clperm, values, off, array);
6423       } break;
6424     default:
6425       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6426     }
6427     PetscCall(PetscSectionRestorePointSyms(section,numPoints,points,&perms,&flips));
6428   }
6429   /* Cleanup points */
6430   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6431   /* Cleanup array */
6432   PetscCall(VecRestoreArray(v, &array));
6433   PetscFunctionReturn(0);
6434 }
6435 
6436 /* Check whether the given point is in the label. If not, update the offset to skip this point */
6437 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset)
6438 {
6439   PetscFunctionBegin;
6440   if (label) {
6441     PetscBool contains;
6442     PetscInt  fdof;
6443 
6444     PetscCall(DMLabelStratumHasPoint(label, labelId, point, &contains));
6445     if (!contains) {
6446       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6447       *offset += fdof;
6448       PetscFunctionReturn(1);
6449     }
6450   }
6451   PetscFunctionReturn(0);
6452 }
6453 
6454 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
6455 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)
6456 {
6457   PetscSection    clSection;
6458   IS              clPoints;
6459   PetscScalar    *array;
6460   PetscInt       *points = NULL;
6461   const PetscInt *clp;
6462   PetscInt        numFields, numPoints, p;
6463   PetscInt        offset = 0, f;
6464 
6465   PetscFunctionBeginHot;
6466   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6467   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6468   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6469   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6470   PetscCall(PetscSectionGetNumFields(section, &numFields));
6471   /* Get points */
6472   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6473   /* Get array */
6474   PetscCall(VecGetArray(v, &array));
6475   /* Get values */
6476   for (f = 0; f < numFields; ++f) {
6477     const PetscInt    **perms = NULL;
6478     const PetscScalar **flips = NULL;
6479 
6480     if (!fieldActive[f]) {
6481       for (p = 0; p < numPoints*2; p += 2) {
6482         PetscInt fdof;
6483         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
6484         offset += fdof;
6485       }
6486       continue;
6487     }
6488     PetscCall(PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6489     switch (mode) {
6490     case INSERT_VALUES:
6491       for (p = 0; p < numPoints; p++) {
6492         const PetscInt    point = points[2*p];
6493         const PetscInt    *perm = perms ? perms[p] : NULL;
6494         const PetscScalar *flip = flips ? flips[p] : NULL;
6495         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6496         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
6497       } break;
6498     case INSERT_ALL_VALUES:
6499       for (p = 0; p < numPoints; p++) {
6500         const PetscInt    point = points[2*p];
6501         const PetscInt    *perm = perms ? perms[p] : NULL;
6502         const PetscScalar *flip = flips ? flips[p] : NULL;
6503         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6504         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
6505       } break;
6506     case INSERT_BC_VALUES:
6507       for (p = 0; p < numPoints; p++) {
6508         const PetscInt    point = points[2*p];
6509         const PetscInt    *perm = perms ? perms[p] : NULL;
6510         const PetscScalar *flip = flips ? flips[p] : NULL;
6511         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6512         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
6513       } break;
6514     case ADD_VALUES:
6515       for (p = 0; p < numPoints; p++) {
6516         const PetscInt    point = points[2*p];
6517         const PetscInt    *perm = perms ? perms[p] : NULL;
6518         const PetscScalar *flip = flips ? flips[p] : NULL;
6519         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6520         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
6521       } break;
6522     case ADD_ALL_VALUES:
6523       for (p = 0; p < numPoints; p++) {
6524         const PetscInt    point = points[2*p];
6525         const PetscInt    *perm = perms ? perms[p] : NULL;
6526         const PetscScalar *flip = flips ? flips[p] : NULL;
6527         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6528         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
6529       } break;
6530     default:
6531       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6532     }
6533     PetscCall(PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6534   }
6535   /* Cleanup points */
6536   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6537   /* Cleanup array */
6538   PetscCall(VecRestoreArray(v, &array));
6539   PetscFunctionReturn(0);
6540 }
6541 
6542 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
6543 {
6544   PetscMPIInt    rank;
6545   PetscInt       i, j;
6546 
6547   PetscFunctionBegin;
6548   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
6549   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
6550   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
6551   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
6552   numCIndices = numCIndices ? numCIndices : numRIndices;
6553   if (!values) PetscFunctionReturn(0);
6554   for (i = 0; i < numRIndices; i++) {
6555     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
6556     for (j = 0; j < numCIndices; j++) {
6557 #if defined(PETSC_USE_COMPLEX)
6558       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i*numCIndices+j]), (double)PetscImaginaryPart(values[i*numCIndices+j])));
6559 #else
6560       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i*numCIndices+j]));
6561 #endif
6562     }
6563     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
6564   }
6565   PetscFunctionReturn(0);
6566 }
6567 
6568 /*
6569   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
6570 
6571   Input Parameters:
6572 + section - The section for this data layout
6573 . islocal - Is the section (and thus indices being requested) local or global?
6574 . point   - The point contributing dofs with these indices
6575 . off     - The global offset of this point
6576 . loff    - The local offset of each field
6577 . setBC   - The flag determining whether to include indices of boundary values
6578 . perm    - A permutation of the dofs on this point, or NULL
6579 - indperm - A permutation of the entire indices array, or NULL
6580 
6581   Output Parameter:
6582 . indices - Indices for dofs on this point
6583 
6584   Level: developer
6585 
6586   Note: The indices could be local or global, depending on the value of 'off'.
6587 */
6588 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal,PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
6589 {
6590   PetscInt        dof;   /* The number of unknowns on this point */
6591   PetscInt        cdof;  /* The number of constraints on this point */
6592   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6593   PetscInt        cind = 0, k;
6594 
6595   PetscFunctionBegin;
6596   PetscCheck(islocal || !setBC,PetscObjectComm((PetscObject)section),PETSC_ERR_ARG_INCOMP,"setBC incompatible with global indices; use a local section or disable setBC");
6597   PetscCall(PetscSectionGetDof(section, point, &dof));
6598   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6599   if (!cdof || setBC) {
6600     for (k = 0; k < dof; ++k) {
6601       const PetscInt preind = perm ? *loff+perm[k] : *loff+k;
6602       const PetscInt ind    = indperm ? indperm[preind] : preind;
6603 
6604       indices[ind] = off + k;
6605     }
6606   } else {
6607     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6608     for (k = 0; k < dof; ++k) {
6609       const PetscInt preind = perm ? *loff+perm[k] : *loff+k;
6610       const PetscInt ind    = indperm ? indperm[preind] : preind;
6611 
6612       if ((cind < cdof) && (k == cdofs[cind])) {
6613         /* Insert check for returning constrained indices */
6614         indices[ind] = -(off+k+1);
6615         ++cind;
6616       } else {
6617         indices[ind] = off + k - (islocal ? 0 : cind);
6618       }
6619     }
6620   }
6621   *loff += dof;
6622   PetscFunctionReturn(0);
6623 }
6624 
6625 /*
6626  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
6627 
6628  Input Parameters:
6629 + section - a section (global or local)
6630 - islocal - PETSC_TRUE if requesting local indices (i.e., section is local); PETSC_FALSE for global
6631 . point - point within section
6632 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
6633 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
6634 . setBC - identify constrained (boundary condition) points via involution.
6635 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
6636 . permsoff - offset
6637 - indperm - index permutation
6638 
6639  Output Parameter:
6640 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
6641 . indices - array to hold indices (as defined by section) of each dof associated with point
6642 
6643  Notes:
6644  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
6645  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
6646  in the local vector.
6647 
6648  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
6649  significant).  It is invalid to call with a global section and setBC=true.
6650 
6651  Developer Note:
6652  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
6653  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
6654  offset could be obtained from the section instead of passing it explicitly as we do now.
6655 
6656  Example:
6657  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
6658  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
6659  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
6660  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.
6661 
6662  Level: developer
6663 */
6664 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[])
6665 {
6666   PetscInt       numFields, foff, f;
6667 
6668   PetscFunctionBegin;
6669   PetscCheck(islocal || !setBC,PetscObjectComm((PetscObject)section),PETSC_ERR_ARG_INCOMP,"setBC incompatible with global indices; use a local section or disable setBC");
6670   PetscCall(PetscSectionGetNumFields(section, &numFields));
6671   for (f = 0, foff = 0; f < numFields; ++f) {
6672     PetscInt        fdof, cfdof;
6673     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6674     PetscInt        cind = 0, b;
6675     const PetscInt  *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6676 
6677     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6678     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
6679     if (!cfdof || setBC) {
6680       for (b = 0; b < fdof; ++b) {
6681         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6682         const PetscInt ind    = indperm ? indperm[preind] : preind;
6683 
6684         indices[ind] = off+foff+b;
6685       }
6686     } else {
6687       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6688       for (b = 0; b < fdof; ++b) {
6689         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6690         const PetscInt ind    = indperm ? indperm[preind] : preind;
6691 
6692         if ((cind < cfdof) && (b == fcdofs[cind])) {
6693           indices[ind] = -(off+foff+b+1);
6694           ++cind;
6695         } else {
6696           indices[ind] = off + foff + b - (islocal ? 0 : cind);
6697         }
6698       }
6699     }
6700     foff     += (setBC || islocal ? fdof : (fdof - cfdof));
6701     foffs[f] += fdof;
6702   }
6703   PetscFunctionReturn(0);
6704 }
6705 
6706 /*
6707   This version believes the globalSection offsets for each field, rather than just the point offset
6708 
6709  . foffs - The offset into 'indices' for each field, since it is segregated by field
6710 
6711  Notes:
6712  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
6713  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
6714 */
6715 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
6716 {
6717   PetscInt       numFields, foff, f;
6718 
6719   PetscFunctionBegin;
6720   PetscCall(PetscSectionGetNumFields(section, &numFields));
6721   for (f = 0; f < numFields; ++f) {
6722     PetscInt        fdof, cfdof;
6723     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6724     PetscInt        cind = 0, b;
6725     const PetscInt  *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6726 
6727     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6728     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
6729     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
6730     if (!cfdof) {
6731       for (b = 0; b < fdof; ++b) {
6732         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6733         const PetscInt ind    = indperm ? indperm[preind] : preind;
6734 
6735         indices[ind] = foff+b;
6736       }
6737     } else {
6738       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6739       for (b = 0; b < fdof; ++b) {
6740         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6741         const PetscInt ind    = indperm ? indperm[preind] : preind;
6742 
6743         if ((cind < cfdof) && (b == fcdofs[cind])) {
6744           indices[ind] = -(foff+b+1);
6745           ++cind;
6746         } else {
6747           indices[ind] = foff+b-cind;
6748         }
6749       }
6750     }
6751     foffs[f] += fdof;
6752   }
6753   PetscFunctionReturn(0);
6754 }
6755 
6756 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)
6757 {
6758   Mat             cMat;
6759   PetscSection    aSec, cSec;
6760   IS              aIS;
6761   PetscInt        aStart = -1, aEnd = -1;
6762   const PetscInt  *anchors;
6763   PetscInt        numFields, f, p, q, newP = 0;
6764   PetscInt        newNumPoints = 0, newNumIndices = 0;
6765   PetscInt        *newPoints, *indices, *newIndices;
6766   PetscInt        maxAnchor, maxDof;
6767   PetscInt        newOffsets[32];
6768   PetscInt        *pointMatOffsets[32];
6769   PetscInt        *newPointOffsets[32];
6770   PetscScalar     *pointMat[32];
6771   PetscScalar     *newValues=NULL,*tmpValues;
6772   PetscBool       anyConstrained = PETSC_FALSE;
6773 
6774   PetscFunctionBegin;
6775   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6776   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6777   PetscCall(PetscSectionGetNumFields(section, &numFields));
6778 
6779   PetscCall(DMPlexGetAnchors(dm,&aSec,&aIS));
6780   /* if there are point-to-point constraints */
6781   if (aSec) {
6782     PetscCall(PetscArrayzero(newOffsets, 32));
6783     PetscCall(ISGetIndices(aIS,&anchors));
6784     PetscCall(PetscSectionGetChart(aSec,&aStart,&aEnd));
6785     /* figure out how many points are going to be in the new element matrix
6786      * (we allow double counting, because it's all just going to be summed
6787      * into the global matrix anyway) */
6788     for (p = 0; p < 2*numPoints; p+=2) {
6789       PetscInt b    = points[p];
6790       PetscInt bDof = 0, bSecDof;
6791 
6792       PetscCall(PetscSectionGetDof(section,b,&bSecDof));
6793       if (!bSecDof) {
6794         continue;
6795       }
6796       if (b >= aStart && b < aEnd) {
6797         PetscCall(PetscSectionGetDof(aSec,b,&bDof));
6798       }
6799       if (bDof) {
6800         /* this point is constrained */
6801         /* it is going to be replaced by its anchors */
6802         PetscInt bOff, q;
6803 
6804         anyConstrained = PETSC_TRUE;
6805         newNumPoints  += bDof;
6806         PetscCall(PetscSectionGetOffset(aSec,b,&bOff));
6807         for (q = 0; q < bDof; q++) {
6808           PetscInt a = anchors[bOff + q];
6809           PetscInt aDof;
6810 
6811           PetscCall(PetscSectionGetDof(section,a,&aDof));
6812           newNumIndices += aDof;
6813           for (f = 0; f < numFields; ++f) {
6814             PetscInt fDof;
6815 
6816             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
6817             newOffsets[f+1] += fDof;
6818           }
6819         }
6820       }
6821       else {
6822         /* this point is not constrained */
6823         newNumPoints++;
6824         newNumIndices += bSecDof;
6825         for (f = 0; f < numFields; ++f) {
6826           PetscInt fDof;
6827 
6828           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6829           newOffsets[f+1] += fDof;
6830         }
6831       }
6832     }
6833   }
6834   if (!anyConstrained) {
6835     if (outNumPoints)  *outNumPoints  = 0;
6836     if (outNumIndices) *outNumIndices = 0;
6837     if (outPoints)     *outPoints     = NULL;
6838     if (outValues)     *outValues     = NULL;
6839     if (aSec) PetscCall(ISRestoreIndices(aIS,&anchors));
6840     PetscFunctionReturn(0);
6841   }
6842 
6843   if (outNumPoints)  *outNumPoints  = newNumPoints;
6844   if (outNumIndices) *outNumIndices = newNumIndices;
6845 
6846   for (f = 0; f < numFields; ++f) newOffsets[f+1] += newOffsets[f];
6847 
6848   if (!outPoints && !outValues) {
6849     if (offsets) {
6850       for (f = 0; f <= numFields; f++) {
6851         offsets[f] = newOffsets[f];
6852       }
6853     }
6854     if (aSec) PetscCall(ISRestoreIndices(aIS,&anchors));
6855     PetscFunctionReturn(0);
6856   }
6857 
6858   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
6859 
6860   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
6861 
6862   /* workspaces */
6863   if (numFields) {
6864     for (f = 0; f < numFields; f++) {
6865       PetscCall(DMGetWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[f]));
6866       PetscCall(DMGetWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[f]));
6867     }
6868   }
6869   else {
6870     PetscCall(DMGetWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[0]));
6871     PetscCall(DMGetWorkArray(dm,numPoints,MPIU_INT,&newPointOffsets[0]));
6872   }
6873 
6874   /* get workspaces for the point-to-point matrices */
6875   if (numFields) {
6876     PetscInt totalOffset, totalMatOffset;
6877 
6878     for (p = 0; p < numPoints; p++) {
6879       PetscInt b    = points[2*p];
6880       PetscInt bDof = 0, bSecDof;
6881 
6882       PetscCall(PetscSectionGetDof(section,b,&bSecDof));
6883       if (!bSecDof) {
6884         for (f = 0; f < numFields; f++) {
6885           newPointOffsets[f][p + 1] = 0;
6886           pointMatOffsets[f][p + 1] = 0;
6887         }
6888         continue;
6889       }
6890       if (b >= aStart && b < aEnd) {
6891         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6892       }
6893       if (bDof) {
6894         for (f = 0; f < numFields; f++) {
6895           PetscInt fDof, q, bOff, allFDof = 0;
6896 
6897           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6898           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6899           for (q = 0; q < bDof; q++) {
6900             PetscInt a = anchors[bOff + q];
6901             PetscInt aFDof;
6902 
6903             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
6904             allFDof += aFDof;
6905           }
6906           newPointOffsets[f][p+1] = allFDof;
6907           pointMatOffsets[f][p+1] = fDof * allFDof;
6908         }
6909       }
6910       else {
6911         for (f = 0; f < numFields; f++) {
6912           PetscInt fDof;
6913 
6914           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6915           newPointOffsets[f][p+1] = fDof;
6916           pointMatOffsets[f][p+1] = 0;
6917         }
6918       }
6919     }
6920     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
6921       newPointOffsets[f][0] = totalOffset;
6922       pointMatOffsets[f][0] = totalMatOffset;
6923       for (p = 0; p < numPoints; p++) {
6924         newPointOffsets[f][p+1] += newPointOffsets[f][p];
6925         pointMatOffsets[f][p+1] += pointMatOffsets[f][p];
6926       }
6927       totalOffset    = newPointOffsets[f][numPoints];
6928       totalMatOffset = pointMatOffsets[f][numPoints];
6929       PetscCall(DMGetWorkArray(dm,pointMatOffsets[f][numPoints],MPIU_SCALAR,&pointMat[f]));
6930     }
6931   }
6932   else {
6933     for (p = 0; p < numPoints; p++) {
6934       PetscInt b    = points[2*p];
6935       PetscInt bDof = 0, bSecDof;
6936 
6937       PetscCall(PetscSectionGetDof(section,b,&bSecDof));
6938       if (!bSecDof) {
6939         newPointOffsets[0][p + 1] = 0;
6940         pointMatOffsets[0][p + 1] = 0;
6941         continue;
6942       }
6943       if (b >= aStart && b < aEnd) {
6944         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6945       }
6946       if (bDof) {
6947         PetscInt bOff, q, allDof = 0;
6948 
6949         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6950         for (q = 0; q < bDof; q++) {
6951           PetscInt a = anchors[bOff + q], aDof;
6952 
6953           PetscCall(PetscSectionGetDof(section, a, &aDof));
6954           allDof += aDof;
6955         }
6956         newPointOffsets[0][p+1] = allDof;
6957         pointMatOffsets[0][p+1] = bSecDof * allDof;
6958       }
6959       else {
6960         newPointOffsets[0][p+1] = bSecDof;
6961         pointMatOffsets[0][p+1] = 0;
6962       }
6963     }
6964     newPointOffsets[0][0] = 0;
6965     pointMatOffsets[0][0] = 0;
6966     for (p = 0; p < numPoints; p++) {
6967       newPointOffsets[0][p+1] += newPointOffsets[0][p];
6968       pointMatOffsets[0][p+1] += pointMatOffsets[0][p];
6969     }
6970     PetscCall(DMGetWorkArray(dm,pointMatOffsets[0][numPoints],MPIU_SCALAR,&pointMat[0]));
6971   }
6972 
6973   /* output arrays */
6974   PetscCall(DMGetWorkArray(dm,2*newNumPoints,MPIU_INT,&newPoints));
6975 
6976   /* get the point-to-point matrices; construct newPoints */
6977   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
6978   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
6979   PetscCall(DMGetWorkArray(dm,maxDof,MPIU_INT,&indices));
6980   PetscCall(DMGetWorkArray(dm,maxAnchor*maxDof,MPIU_INT,&newIndices));
6981   if (numFields) {
6982     for (p = 0, newP = 0; p < numPoints; p++) {
6983       PetscInt b    = points[2*p];
6984       PetscInt o    = points[2*p+1];
6985       PetscInt bDof = 0, bSecDof;
6986 
6987       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
6988       if (!bSecDof) {
6989         continue;
6990       }
6991       if (b >= aStart && b < aEnd) {
6992         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6993       }
6994       if (bDof) {
6995         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
6996 
6997         fStart[0] = 0;
6998         fEnd[0]   = 0;
6999         for (f = 0; f < numFields; f++) {
7000           PetscInt fDof;
7001 
7002           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
7003           fStart[f+1] = fStart[f] + fDof;
7004           fEnd[f+1]   = fStart[f+1];
7005         }
7006         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7007         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7008 
7009         fAnchorStart[0] = 0;
7010         fAnchorEnd[0]   = 0;
7011         for (f = 0; f < numFields; f++) {
7012           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7013 
7014           fAnchorStart[f+1] = fAnchorStart[f] + fDof;
7015           fAnchorEnd[f+1]   = fAnchorStart[f + 1];
7016         }
7017         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7018         for (q = 0; q < bDof; q++) {
7019           PetscInt a = anchors[bOff + q], aOff;
7020 
7021           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7022           newPoints[2*(newP + q)]     = a;
7023           newPoints[2*(newP + q) + 1] = 0;
7024           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7025           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7026         }
7027         newP += bDof;
7028 
7029         if (outValues) {
7030           /* get the point-to-point submatrix */
7031           for (f = 0; f < numFields; f++) {
7032             PetscCall(MatGetValues(cMat,fEnd[f]-fStart[f],indices + fStart[f],fAnchorEnd[f] - fAnchorStart[f],newIndices + fAnchorStart[f],pointMat[f] + pointMatOffsets[f][p]));
7033           }
7034         }
7035       }
7036       else {
7037         newPoints[2 * newP]     = b;
7038         newPoints[2 * newP + 1] = o;
7039         newP++;
7040       }
7041     }
7042   } else {
7043     for (p = 0; p < numPoints; p++) {
7044       PetscInt b    = points[2*p];
7045       PetscInt o    = points[2*p+1];
7046       PetscInt bDof = 0, bSecDof;
7047 
7048       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7049       if (!bSecDof) {
7050         continue;
7051       }
7052       if (b >= aStart && b < aEnd) {
7053         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7054       }
7055       if (bDof) {
7056         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7057 
7058         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7059         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7060 
7061         PetscCall(PetscSectionGetOffset (aSec, b, &bOff));
7062         for (q = 0; q < bDof; q++) {
7063           PetscInt a = anchors[bOff + q], aOff;
7064 
7065           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7066 
7067           newPoints[2*(newP + q)]     = a;
7068           newPoints[2*(newP + q) + 1] = 0;
7069           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7070           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7071         }
7072         newP += bDof;
7073 
7074         /* get the point-to-point submatrix */
7075         if (outValues) {
7076           PetscCall(MatGetValues(cMat,bEnd,indices,bAnchorEnd,newIndices,pointMat[0] + pointMatOffsets[0][p]));
7077         }
7078       }
7079       else {
7080         newPoints[2 * newP]     = b;
7081         newPoints[2 * newP + 1] = o;
7082         newP++;
7083       }
7084     }
7085   }
7086 
7087   if (outValues) {
7088     PetscCall(DMGetWorkArray(dm,newNumIndices*numIndices,MPIU_SCALAR,&tmpValues));
7089     PetscCall(PetscArrayzero(tmpValues,newNumIndices*numIndices));
7090     /* multiply constraints on the right */
7091     if (numFields) {
7092       for (f = 0; f < numFields; f++) {
7093         PetscInt oldOff = offsets[f];
7094 
7095         for (p = 0; p < numPoints; p++) {
7096           PetscInt cStart = newPointOffsets[f][p];
7097           PetscInt b      = points[2 * p];
7098           PetscInt c, r, k;
7099           PetscInt dof;
7100 
7101           PetscCall(PetscSectionGetFieldDof(section,b,f,&dof));
7102           if (!dof) {
7103             continue;
7104           }
7105           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7106             PetscInt nCols         = newPointOffsets[f][p+1]-cStart;
7107             const PetscScalar *mat = pointMat[f] + pointMatOffsets[f][p];
7108 
7109             for (r = 0; r < numIndices; r++) {
7110               for (c = 0; c < nCols; c++) {
7111                 for (k = 0; k < dof; k++) {
7112                   tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7113                 }
7114               }
7115             }
7116           }
7117           else {
7118             /* copy this column as is */
7119             for (r = 0; r < numIndices; r++) {
7120               for (c = 0; c < dof; c++) {
7121                 tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7122               }
7123             }
7124           }
7125           oldOff += dof;
7126         }
7127       }
7128     }
7129     else {
7130       PetscInt oldOff = 0;
7131       for (p = 0; p < numPoints; p++) {
7132         PetscInt cStart = newPointOffsets[0][p];
7133         PetscInt b      = points[2 * p];
7134         PetscInt c, r, k;
7135         PetscInt dof;
7136 
7137         PetscCall(PetscSectionGetDof(section,b,&dof));
7138         if (!dof) {
7139           continue;
7140         }
7141         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7142           PetscInt nCols         = newPointOffsets[0][p+1]-cStart;
7143           const PetscScalar *mat = pointMat[0] + pointMatOffsets[0][p];
7144 
7145           for (r = 0; r < numIndices; r++) {
7146             for (c = 0; c < nCols; c++) {
7147               for (k = 0; k < dof; k++) {
7148                 tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7149               }
7150             }
7151           }
7152         }
7153         else {
7154           /* copy this column as is */
7155           for (r = 0; r < numIndices; r++) {
7156             for (c = 0; c < dof; c++) {
7157               tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7158             }
7159           }
7160         }
7161         oldOff += dof;
7162       }
7163     }
7164 
7165     if (multiplyLeft) {
7166       PetscCall(DMGetWorkArray(dm,newNumIndices*newNumIndices,MPIU_SCALAR,&newValues));
7167       PetscCall(PetscArrayzero(newValues,newNumIndices*newNumIndices));
7168       /* multiply constraints transpose on the left */
7169       if (numFields) {
7170         for (f = 0; f < numFields; f++) {
7171           PetscInt oldOff = offsets[f];
7172 
7173           for (p = 0; p < numPoints; p++) {
7174             PetscInt rStart = newPointOffsets[f][p];
7175             PetscInt b      = points[2 * p];
7176             PetscInt c, r, k;
7177             PetscInt dof;
7178 
7179             PetscCall(PetscSectionGetFieldDof(section,b,f,&dof));
7180             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7181               PetscInt nRows                        = newPointOffsets[f][p+1]-rStart;
7182               const PetscScalar *PETSC_RESTRICT mat = pointMat[f] + pointMatOffsets[f][p];
7183 
7184               for (r = 0; r < nRows; r++) {
7185                 for (c = 0; c < newNumIndices; c++) {
7186                   for (k = 0; k < dof; k++) {
7187                     newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7188                   }
7189                 }
7190               }
7191             }
7192             else {
7193               /* copy this row as is */
7194               for (r = 0; r < dof; r++) {
7195                 for (c = 0; c < newNumIndices; c++) {
7196                   newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7197                 }
7198               }
7199             }
7200             oldOff += dof;
7201           }
7202         }
7203       }
7204       else {
7205         PetscInt oldOff = 0;
7206 
7207         for (p = 0; p < numPoints; p++) {
7208           PetscInt rStart = newPointOffsets[0][p];
7209           PetscInt b      = points[2 * p];
7210           PetscInt c, r, k;
7211           PetscInt dof;
7212 
7213           PetscCall(PetscSectionGetDof(section,b,&dof));
7214           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7215             PetscInt nRows                        = newPointOffsets[0][p+1]-rStart;
7216             const PetscScalar *PETSC_RESTRICT mat = pointMat[0] + pointMatOffsets[0][p];
7217 
7218             for (r = 0; r < nRows; r++) {
7219               for (c = 0; c < newNumIndices; c++) {
7220                 for (k = 0; k < dof; k++) {
7221                   newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7222                 }
7223               }
7224             }
7225           }
7226           else {
7227             /* copy this row as is */
7228             for (r = 0; r < dof; r++) {
7229               for (c = 0; c < newNumIndices; c++) {
7230                 newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7231               }
7232             }
7233           }
7234           oldOff += dof;
7235         }
7236       }
7237 
7238       PetscCall(DMRestoreWorkArray(dm,newNumIndices*numIndices,MPIU_SCALAR,&tmpValues));
7239     }
7240     else {
7241       newValues = tmpValues;
7242     }
7243   }
7244 
7245   /* clean up */
7246   PetscCall(DMRestoreWorkArray(dm,maxDof,MPIU_INT,&indices));
7247   PetscCall(DMRestoreWorkArray(dm,maxAnchor*maxDof,MPIU_INT,&newIndices));
7248 
7249   if (numFields) {
7250     for (f = 0; f < numFields; f++) {
7251       PetscCall(DMRestoreWorkArray(dm,pointMatOffsets[f][numPoints],MPIU_SCALAR,&pointMat[f]));
7252       PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[f]));
7253       PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[f]));
7254     }
7255   }
7256   else {
7257     PetscCall(DMRestoreWorkArray(dm,pointMatOffsets[0][numPoints],MPIU_SCALAR,&pointMat[0]));
7258     PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[0]));
7259     PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[0]));
7260   }
7261   PetscCall(ISRestoreIndices(aIS,&anchors));
7262 
7263   /* output */
7264   if (outPoints) {
7265     *outPoints = newPoints;
7266   }
7267   else {
7268     PetscCall(DMRestoreWorkArray(dm,2*newNumPoints,MPIU_INT,&newPoints));
7269   }
7270   if (outValues) {
7271     *outValues = newValues;
7272   }
7273   for (f = 0; f <= numFields; f++) {
7274     offsets[f] = newOffsets[f];
7275   }
7276   PetscFunctionReturn(0);
7277 }
7278 
7279 /*@C
7280   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7281 
7282   Not collective
7283 
7284   Input Parameters:
7285 + dm         - The DM
7286 . section    - The PetscSection describing the points (a local section)
7287 . idxSection - The PetscSection from which to obtain indices (may be local or global)
7288 . point      - The point defining the closure
7289 - useClPerm  - Use the closure point permutation if available
7290 
7291   Output Parameters:
7292 + numIndices - The number of dof indices in the closure of point with the input sections
7293 . indices    - The dof indices
7294 . outOffsets - Array to write the field offsets into, or NULL
7295 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7296 
7297   Notes:
7298   Must call DMPlexRestoreClosureIndices() to free allocated memory
7299 
7300   If idxSection is global, any constrained dofs (see DMAddBoundary(), for example) will get negative indices.  The value
7301   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7302   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7303   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7304   indices (with the above semantics) are implied.
7305 
7306   Level: advanced
7307 
7308 .seealso `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7309 @*/
7310 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm,
7311                                        PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7312 {
7313   /* Closure ordering */
7314   PetscSection        clSection;
7315   IS                  clPoints;
7316   const PetscInt     *clp;
7317   PetscInt           *points;
7318   const PetscInt     *clperm = NULL;
7319   /* Dof permutation and sign flips */
7320   const PetscInt    **perms[32] = {NULL};
7321   const PetscScalar **flips[32] = {NULL};
7322   PetscScalar        *valCopy   = NULL;
7323   /* Hanging node constraints */
7324   PetscInt           *pointsC = NULL;
7325   PetscScalar        *valuesC = NULL;
7326   PetscInt            NclC, NiC;
7327 
7328   PetscInt           *idx;
7329   PetscInt            Nf, Ncl, Ni = 0, offsets[32], p, f;
7330   PetscBool           isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7331 
7332   PetscFunctionBeginHot;
7333   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7334   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7335   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7336   if (numIndices) PetscValidIntPointer(numIndices, 6);
7337   if (indices)    PetscValidPointer(indices, 7);
7338   if (outOffsets) PetscValidIntPointer(outOffsets, 8);
7339   if (values)     PetscValidPointer(values, 9);
7340   PetscCall(PetscSectionGetNumFields(section, &Nf));
7341   PetscCheck(Nf <= 31,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7342   PetscCall(PetscArrayzero(offsets, 32));
7343   /* 1) Get points in closure */
7344   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7345   if (useClPerm) {
7346     PetscInt depth, clsize;
7347     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7348     for (clsize=0,p=0; p<Ncl; p++) {
7349       PetscInt dof;
7350       PetscCall(PetscSectionGetDof(section, points[2*p], &dof));
7351       clsize += dof;
7352     }
7353     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, clsize, &clperm));
7354   }
7355   /* 2) Get number of indices on these points and field offsets from section */
7356   for (p = 0; p < Ncl*2; p += 2) {
7357     PetscInt dof, fdof;
7358 
7359     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7360     for (f = 0; f < Nf; ++f) {
7361       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7362       offsets[f+1] += fdof;
7363     }
7364     Ni += dof;
7365   }
7366   for (f = 1; f < Nf; ++f) offsets[f+1] += offsets[f];
7367   PetscCheck(!Nf || offsets[Nf] == Ni,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7368   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7369   for (f = 0; f < PetscMax(1, Nf); ++f) {
7370     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7371     else    PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7372     /* may need to apply sign changes to the element matrix */
7373     if (values && flips[f]) {
7374       PetscInt foffset = offsets[f];
7375 
7376       for (p = 0; p < Ncl; ++p) {
7377         PetscInt           pnt  = points[2*p], fdof;
7378         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7379 
7380         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7381         else     PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7382         if (flip) {
7383           PetscInt i, j, k;
7384 
7385           if (!valCopy) {
7386             PetscCall(DMGetWorkArray(dm, Ni*Ni, MPIU_SCALAR, &valCopy));
7387             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7388             *values = valCopy;
7389           }
7390           for (i = 0; i < fdof; ++i) {
7391             PetscScalar fval = flip[i];
7392 
7393             for (k = 0; k < Ni; ++k) {
7394               valCopy[Ni * (foffset + i) + k] *= fval;
7395               valCopy[Ni * k + (foffset + i)] *= fval;
7396             }
7397           }
7398         }
7399         foffset += fdof;
7400       }
7401     }
7402   }
7403   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7404   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7405   if (NclC) {
7406     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni*Ni, MPIU_SCALAR, &valCopy));
7407     for (f = 0; f < PetscMax(1, Nf); ++f) {
7408       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7409       else    PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7410     }
7411     for (f = 0; f < PetscMax(1, Nf); ++f) {
7412       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7413       else    PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7414     }
7415     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7416     Ncl     = NclC;
7417     Ni      = NiC;
7418     points  = pointsC;
7419     if (values) *values = valuesC;
7420   }
7421   /* 5) Calculate indices */
7422   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7423   if (Nf) {
7424     PetscInt  idxOff;
7425     PetscBool useFieldOffsets;
7426 
7427     if (outOffsets) {for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];}
7428     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7429     if (useFieldOffsets) {
7430       for (p = 0; p < Ncl; ++p) {
7431         const PetscInt pnt = points[p*2];
7432 
7433         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7434       }
7435     } else {
7436       for (p = 0; p < Ncl; ++p) {
7437         const PetscInt pnt = points[p*2];
7438 
7439         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7440         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7441          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7442          * global section. */
7443         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff+1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7444       }
7445     }
7446   } else {
7447     PetscInt off = 0, idxOff;
7448 
7449     for (p = 0; p < Ncl; ++p) {
7450       const PetscInt  pnt  = points[p*2];
7451       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7452 
7453       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7454       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7455        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7456       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff+1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7457     }
7458   }
7459   /* 6) Cleanup */
7460   for (f = 0; f < PetscMax(1, Nf); ++f) {
7461     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7462     else    PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7463   }
7464   if (NclC) {
7465     PetscCall(DMRestoreWorkArray(dm, NclC*2, MPIU_INT, &pointsC));
7466   } else {
7467     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7468   }
7469 
7470   if (numIndices) *numIndices = Ni;
7471   if (indices)    *indices    = idx;
7472   PetscFunctionReturn(0);
7473 }
7474 
7475 /*@C
7476   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7477 
7478   Not collective
7479 
7480   Input Parameters:
7481 + dm         - The DM
7482 . section    - The PetscSection describing the points (a local section)
7483 . idxSection - The PetscSection from which to obtain indices (may be local or global)
7484 . point      - The point defining the closure
7485 - useClPerm  - Use the closure point permutation if available
7486 
7487   Output Parameters:
7488 + numIndices - The number of dof indices in the closure of point with the input sections
7489 . indices    - The dof indices
7490 . outOffsets - Array to write the field offsets into, or NULL
7491 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7492 
7493   Notes:
7494   If values were modified, the user is responsible for calling DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values).
7495 
7496   If idxSection is global, any constrained dofs (see DMAddBoundary(), for example) will get negative indices.  The value
7497   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7498   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7499   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7500   indices (with the above semantics) are implied.
7501 
7502   Level: advanced
7503 
7504 .seealso `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7505 @*/
7506 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm,
7507                                            PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7508 {
7509   PetscFunctionBegin;
7510   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7511   PetscValidPointer(indices, 7);
7512   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
7513   PetscFunctionReturn(0);
7514 }
7515 
7516 /*@C
7517   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
7518 
7519   Not collective
7520 
7521   Input Parameters:
7522 + dm - The DM
7523 . section - The section describing the layout in v, or NULL to use the default section
7524 . globalSection - The section describing the layout in v, or NULL to use the default global section
7525 . A - The matrix
7526 . point - The point in the DM
7527 . values - The array of values
7528 - mode - The insert mode, where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions
7529 
7530   Fortran Notes:
7531   This routine is only available in Fortran 90, and you must include petsc.h90 in your code.
7532 
7533   Level: intermediate
7534 
7535 .seealso `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7536 @*/
7537 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7538 {
7539   DM_Plex           *mesh = (DM_Plex*) dm->data;
7540   PetscInt          *indices;
7541   PetscInt           numIndices;
7542   const PetscScalar *valuesOrig = values;
7543   PetscErrorCode     ierr;
7544 
7545   PetscFunctionBegin;
7546   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7547   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7548   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7549   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
7550   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
7551   PetscValidHeaderSpecific(A, MAT_CLASSID, 4);
7552 
7553   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **) &values));
7554 
7555   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
7556   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7557   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
7558   if (ierr) {
7559     PetscMPIInt    rank;
7560 
7561     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7562     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7563     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
7564     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **) &values));
7565     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7566     SETERRQ(PetscObjectComm((PetscObject)dm),ierr,"Not possible to set matrix values");
7567   }
7568   if (mesh->printFEM > 1) {
7569     PetscInt i;
7570     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
7571     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
7572     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
7573   }
7574 
7575   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **) &values));
7576   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7577   PetscFunctionReturn(0);
7578 }
7579 
7580 /*@C
7581   DMPlexMatSetClosure - Set an array of the values on the closure of 'point' using a different row and column section
7582 
7583   Not collective
7584 
7585   Input Parameters:
7586 + dmRow - The DM for the row fields
7587 . sectionRow - The section describing the layout, or NULL to use the default section in dmRow
7588 . globalSectionRow - The section describing the layout, or NULL to use the default global section in dmRow
7589 . dmCol - The DM for the column fields
7590 . sectionCol - The section describing the layout, or NULL to use the default section in dmCol
7591 . globalSectionCol - The section describing the layout, or NULL to use the default global section in dmCol
7592 . A - The matrix
7593 . point - The point in the DMs
7594 . values - The array of values
7595 - mode - The insert mode, where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions
7596 
7597   Level: intermediate
7598 
7599 .seealso `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7600 @*/
7601 PetscErrorCode DMPlexMatSetClosureGeneral(DM dmRow, PetscSection sectionRow, PetscSection globalSectionRow, DM dmCol, PetscSection sectionCol, PetscSection globalSectionCol, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7602 {
7603   DM_Plex           *mesh = (DM_Plex*) dmRow->data;
7604   PetscInt          *indicesRow, *indicesCol;
7605   PetscInt           numIndicesRow, numIndicesCol;
7606   const PetscScalar *valuesOrig = values;
7607   PetscErrorCode     ierr;
7608 
7609   PetscFunctionBegin;
7610   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
7611   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
7612   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
7613   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
7614   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
7615   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 4);
7616   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
7617   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 5);
7618   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
7619   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 6);
7620   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7621 
7622   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values));
7623   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **) &values));
7624 
7625   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7626   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7627   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
7628   if (ierr) {
7629     PetscMPIInt    rank;
7630 
7631     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7632     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7633     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7634     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values));
7635     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **) &values));
7636     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7637   }
7638 
7639   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values));
7640   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **) &values));
7641   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7642   PetscFunctionReturn(0);
7643 }
7644 
7645 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7646 {
7647   DM_Plex        *mesh   = (DM_Plex*) dmf->data;
7648   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7649   PetscInt       *cpoints = NULL;
7650   PetscInt       *findices, *cindices;
7651   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7652   PetscInt        foffsets[32], coffsets[32];
7653   DMPolytopeType  ct;
7654   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7655   PetscErrorCode  ierr;
7656 
7657   PetscFunctionBegin;
7658   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7659   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7660   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7661   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7662   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7663   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7664   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7665   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7666   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7667   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7668   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7669   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7670   PetscCheck(numFields <= 31,PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7671   PetscCall(PetscArrayzero(foffsets, 32));
7672   PetscCall(PetscArrayzero(coffsets, 32));
7673   /* Column indices */
7674   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7675   maxFPoints = numCPoints;
7676   /* Compress out points not in the section */
7677   /*   TODO: Squeeze out points with 0 dof as well */
7678   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7679   for (p = 0, q = 0; p < numCPoints*2; p += 2) {
7680     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7681       cpoints[q*2]   = cpoints[p];
7682       cpoints[q*2+1] = cpoints[p+1];
7683       ++q;
7684     }
7685   }
7686   numCPoints = q;
7687   for (p = 0, numCIndices = 0; p < numCPoints*2; p += 2) {
7688     PetscInt fdof;
7689 
7690     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7691     if (!dof) continue;
7692     for (f = 0; f < numFields; ++f) {
7693       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7694       coffsets[f+1] += fdof;
7695     }
7696     numCIndices += dof;
7697   }
7698   for (f = 1; f < numFields; ++f) coffsets[f+1] += coffsets[f];
7699   /* Row indices */
7700   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7701   {
7702     DMPlexTransform tr;
7703     DMPolytopeType *rct;
7704     PetscInt       *rsize, *rcone, *rornt, Nt;
7705 
7706     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7707     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7708     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7709     numSubcells = rsize[Nt-1];
7710     PetscCall(DMPlexTransformDestroy(&tr));
7711   }
7712   PetscCall(DMGetWorkArray(dmf, maxFPoints*2*numSubcells, MPIU_INT, &ftotpoints));
7713   for (r = 0, q = 0; r < numSubcells; ++r) {
7714     /* TODO Map from coarse to fine cells */
7715     PetscCall(DMPlexGetTransitiveClosure(dmf, point*numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7716     /* Compress out points not in the section */
7717     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7718     for (p = 0; p < numFPoints*2; p += 2) {
7719       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7720         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7721         if (!dof) continue;
7722         for (s = 0; s < q; ++s) if (fpoints[p] == ftotpoints[s*2]) break;
7723         if (s < q) continue;
7724         ftotpoints[q*2]   = fpoints[p];
7725         ftotpoints[q*2+1] = fpoints[p+1];
7726         ++q;
7727       }
7728     }
7729     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7730   }
7731   numFPoints = q;
7732   for (p = 0, numFIndices = 0; p < numFPoints*2; p += 2) {
7733     PetscInt fdof;
7734 
7735     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
7736     if (!dof) continue;
7737     for (f = 0; f < numFields; ++f) {
7738       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
7739       foffsets[f+1] += fdof;
7740     }
7741     numFIndices += dof;
7742   }
7743   for (f = 1; f < numFields; ++f) foffsets[f+1] += foffsets[f];
7744 
7745   PetscCheck(!numFields || foffsets[numFields] == numFIndices,PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
7746   PetscCheck(!numFields || coffsets[numFields] == numCIndices,PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
7747   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7748   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7749   if (numFields) {
7750     const PetscInt **permsF[32] = {NULL};
7751     const PetscInt **permsC[32] = {NULL};
7752 
7753     for (f = 0; f < numFields; f++) {
7754       PetscCall(PetscSectionGetFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7755       PetscCall(PetscSectionGetFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7756     }
7757     for (p = 0; p < numFPoints; p++) {
7758       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7759       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
7760     }
7761     for (p = 0; p < numCPoints; p++) {
7762       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7763       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
7764     }
7765     for (f = 0; f < numFields; f++) {
7766       PetscCall(PetscSectionRestoreFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7767       PetscCall(PetscSectionRestoreFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7768     }
7769   } else {
7770     const PetscInt **permsF = NULL;
7771     const PetscInt **permsC = NULL;
7772 
7773     PetscCall(PetscSectionGetPointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7774     PetscCall(PetscSectionGetPointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7775     for (p = 0, off = 0; p < numFPoints; p++) {
7776       const PetscInt *perm = permsF ? permsF[p] : NULL;
7777 
7778       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7779       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
7780     }
7781     for (p = 0, off = 0; p < numCPoints; p++) {
7782       const PetscInt *perm = permsC ? permsC[p] : NULL;
7783 
7784       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7785       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
7786     }
7787     PetscCall(PetscSectionRestorePointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7788     PetscCall(PetscSectionRestorePointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7789   }
7790   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
7791   /* TODO: flips */
7792   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7793   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
7794   if (ierr) {
7795     PetscMPIInt    rank;
7796 
7797     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7798     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7799     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
7800     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7801     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7802   }
7803   PetscCall(DMRestoreWorkArray(dmf, numCPoints*2*4, MPIU_INT, &ftotpoints));
7804   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7805   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7806   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7807   PetscFunctionReturn(0);
7808 }
7809 
7810 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
7811 {
7812   PetscInt      *fpoints = NULL, *ftotpoints = NULL;
7813   PetscInt      *cpoints = NULL;
7814   PetscInt       foffsets[32], coffsets[32];
7815   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7816   DMPolytopeType ct;
7817   PetscInt       numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7818 
7819   PetscFunctionBegin;
7820   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7821   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7822   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7823   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7824   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7825   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7826   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7827   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7828   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7829   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7830   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7831   PetscCheck(numFields <= 31,PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7832   PetscCall(PetscArrayzero(foffsets, 32));
7833   PetscCall(PetscArrayzero(coffsets, 32));
7834   /* Column indices */
7835   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7836   maxFPoints = numCPoints;
7837   /* Compress out points not in the section */
7838   /*   TODO: Squeeze out points with 0 dof as well */
7839   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7840   for (p = 0, q = 0; p < numCPoints*2; p += 2) {
7841     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7842       cpoints[q*2]   = cpoints[p];
7843       cpoints[q*2+1] = cpoints[p+1];
7844       ++q;
7845     }
7846   }
7847   numCPoints = q;
7848   for (p = 0, numCIndices = 0; p < numCPoints*2; p += 2) {
7849     PetscInt fdof;
7850 
7851     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7852     if (!dof) continue;
7853     for (f = 0; f < numFields; ++f) {
7854       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7855       coffsets[f+1] += fdof;
7856     }
7857     numCIndices += dof;
7858   }
7859   for (f = 1; f < numFields; ++f) coffsets[f+1] += coffsets[f];
7860   /* Row indices */
7861   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7862   {
7863     DMPlexTransform tr;
7864     DMPolytopeType *rct;
7865     PetscInt       *rsize, *rcone, *rornt, Nt;
7866 
7867     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7868     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7869     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7870     numSubcells = rsize[Nt-1];
7871     PetscCall(DMPlexTransformDestroy(&tr));
7872   }
7873   PetscCall(DMGetWorkArray(dmf, maxFPoints*2*numSubcells, MPIU_INT, &ftotpoints));
7874   for (r = 0, q = 0; r < numSubcells; ++r) {
7875     /* TODO Map from coarse to fine cells */
7876     PetscCall(DMPlexGetTransitiveClosure(dmf, point*numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7877     /* Compress out points not in the section */
7878     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7879     for (p = 0; p < numFPoints*2; p += 2) {
7880       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7881         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7882         if (!dof) continue;
7883         for (s = 0; s < q; ++s) if (fpoints[p] == ftotpoints[s*2]) break;
7884         if (s < q) continue;
7885         ftotpoints[q*2]   = fpoints[p];
7886         ftotpoints[q*2+1] = fpoints[p+1];
7887         ++q;
7888       }
7889     }
7890     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7891   }
7892   numFPoints = q;
7893   for (p = 0, numFIndices = 0; p < numFPoints*2; p += 2) {
7894     PetscInt fdof;
7895 
7896     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
7897     if (!dof) continue;
7898     for (f = 0; f < numFields; ++f) {
7899       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
7900       foffsets[f+1] += fdof;
7901     }
7902     numFIndices += dof;
7903   }
7904   for (f = 1; f < numFields; ++f) foffsets[f+1] += foffsets[f];
7905 
7906   PetscCheck(!numFields || foffsets[numFields] == numFIndices,PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
7907   PetscCheck(!numFields || coffsets[numFields] == numCIndices,PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
7908   if (numFields) {
7909     const PetscInt **permsF[32] = {NULL};
7910     const PetscInt **permsC[32] = {NULL};
7911 
7912     for (f = 0; f < numFields; f++) {
7913       PetscCall(PetscSectionGetFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7914       PetscCall(PetscSectionGetFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7915     }
7916     for (p = 0; p < numFPoints; p++) {
7917       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7918       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
7919     }
7920     for (p = 0; p < numCPoints; p++) {
7921       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7922       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
7923     }
7924     for (f = 0; f < numFields; f++) {
7925       PetscCall(PetscSectionRestoreFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7926       PetscCall(PetscSectionRestoreFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7927     }
7928   } else {
7929     const PetscInt **permsF = NULL;
7930     const PetscInt **permsC = NULL;
7931 
7932     PetscCall(PetscSectionGetPointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7933     PetscCall(PetscSectionGetPointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7934     for (p = 0, off = 0; p < numFPoints; p++) {
7935       const PetscInt *perm = permsF ? permsF[p] : NULL;
7936 
7937       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7938       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
7939     }
7940     for (p = 0, off = 0; p < numCPoints; p++) {
7941       const PetscInt *perm = permsC ? permsC[p] : NULL;
7942 
7943       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7944       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
7945     }
7946     PetscCall(PetscSectionRestorePointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7947     PetscCall(PetscSectionRestorePointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7948   }
7949   PetscCall(DMRestoreWorkArray(dmf, numCPoints*2*4, MPIU_INT, &ftotpoints));
7950   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7951   PetscFunctionReturn(0);
7952 }
7953 
7954 /*@C
7955   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
7956 
7957   Input Parameter:
7958 . dm   - The DMPlex object
7959 
7960   Output Parameter:
7961 . cellHeight - The height of a cell
7962 
7963   Level: developer
7964 
7965 .seealso `DMPlexSetVTKCellHeight()`
7966 @*/
7967 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
7968 {
7969   DM_Plex *mesh = (DM_Plex*) dm->data;
7970 
7971   PetscFunctionBegin;
7972   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7973   PetscValidIntPointer(cellHeight, 2);
7974   *cellHeight = mesh->vtkCellHeight;
7975   PetscFunctionReturn(0);
7976 }
7977 
7978 /*@C
7979   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
7980 
7981   Input Parameters:
7982 + dm   - The DMPlex object
7983 - cellHeight - The height of a cell
7984 
7985   Level: developer
7986 
7987 .seealso `DMPlexGetVTKCellHeight()`
7988 @*/
7989 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
7990 {
7991   DM_Plex *mesh = (DM_Plex*) dm->data;
7992 
7993   PetscFunctionBegin;
7994   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7995   mesh->vtkCellHeight = cellHeight;
7996   PetscFunctionReturn(0);
7997 }
7998 
7999 /*@
8000   DMPlexGetGhostCellStratum - Get the range of cells which are used to enforce FV boundary conditions
8001 
8002   Input Parameter:
8003 . dm - The DMPlex object
8004 
8005   Output Parameters:
8006 + gcStart - The first ghost cell, or NULL
8007 - gcEnd   - The upper bound on ghost cells, or NULL
8008 
8009   Level: advanced
8010 
8011 .seealso `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
8012 @*/
8013 PetscErrorCode DMPlexGetGhostCellStratum(DM dm, PetscInt *gcStart, PetscInt *gcEnd)
8014 {
8015   DMLabel        ctLabel;
8016 
8017   PetscFunctionBegin;
8018   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8019   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
8020   PetscCall(DMLabelGetStratumBounds(ctLabel, DM_POLYTOPE_FV_GHOST, gcStart, gcEnd));
8021   PetscFunctionReturn(0);
8022 }
8023 
8024 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8025 {
8026   PetscSection   section, globalSection;
8027   PetscInt      *numbers, p;
8028 
8029   PetscFunctionBegin;
8030   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf));
8031   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8032   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8033   for (p = pStart; p < pEnd; ++p) {
8034     PetscCall(PetscSectionSetDof(section, p, 1));
8035   }
8036   PetscCall(PetscSectionSetUp(section));
8037   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8038   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8039   for (p = pStart; p < pEnd; ++p) {
8040     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p-pStart]));
8041     if (numbers[p-pStart] < 0) numbers[p-pStart] -= shift;
8042     else                       numbers[p-pStart] += shift;
8043   }
8044   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject) dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8045   if (globalSize) {
8046     PetscLayout layout;
8047     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject) dm), globalSection, &layout));
8048     PetscCall(PetscLayoutGetSize(layout, globalSize));
8049     PetscCall(PetscLayoutDestroy(&layout));
8050   }
8051   PetscCall(PetscSectionDestroy(&section));
8052   PetscCall(PetscSectionDestroy(&globalSection));
8053   PetscFunctionReturn(0);
8054 }
8055 
8056 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8057 {
8058   PetscInt       cellHeight, cStart, cEnd;
8059 
8060   PetscFunctionBegin;
8061   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8062   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8063   else               PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8064   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8065   PetscFunctionReturn(0);
8066 }
8067 
8068 /*@
8069   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8070 
8071   Input Parameter:
8072 . dm   - The DMPlex object
8073 
8074   Output Parameter:
8075 . globalCellNumbers - Global cell numbers for all cells on this process
8076 
8077   Level: developer
8078 
8079 .seealso `DMPlexGetVertexNumbering()`
8080 @*/
8081 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8082 {
8083   DM_Plex       *mesh = (DM_Plex*) dm->data;
8084 
8085   PetscFunctionBegin;
8086   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8087   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8088   *globalCellNumbers = mesh->globalCellNumbers;
8089   PetscFunctionReturn(0);
8090 }
8091 
8092 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8093 {
8094   PetscInt       vStart, vEnd;
8095 
8096   PetscFunctionBegin;
8097   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8098   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8099   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8100   PetscFunctionReturn(0);
8101 }
8102 
8103 /*@
8104   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8105 
8106   Input Parameter:
8107 . dm   - The DMPlex object
8108 
8109   Output Parameter:
8110 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8111 
8112   Level: developer
8113 
8114 .seealso `DMPlexGetCellNumbering()`
8115 @*/
8116 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8117 {
8118   DM_Plex       *mesh = (DM_Plex*) dm->data;
8119 
8120   PetscFunctionBegin;
8121   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8122   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8123   *globalVertexNumbers = mesh->globalVertexNumbers;
8124   PetscFunctionReturn(0);
8125 }
8126 
8127 /*@
8128   DMPlexCreatePointNumbering - Create a global numbering for all points on this process
8129 
8130   Input Parameter:
8131 . dm   - The DMPlex object
8132 
8133   Output Parameter:
8134 . globalPointNumbers - Global numbers for all points on this process
8135 
8136   Level: developer
8137 
8138 .seealso `DMPlexGetCellNumbering()`
8139 @*/
8140 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8141 {
8142   IS             nums[4];
8143   PetscInt       depths[4], gdepths[4], starts[4];
8144   PetscInt       depth, d, shift = 0;
8145 
8146   PetscFunctionBegin;
8147   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8148   PetscCall(DMPlexGetDepth(dm, &depth));
8149   /* For unstratified meshes use dim instead of depth */
8150   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8151   for (d = 0; d <= depth; ++d) {
8152     PetscInt end;
8153 
8154     depths[d] = depth-d;
8155     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8156     if (!(starts[d]-end)) { starts[d] = depths[d] = -1; }
8157   }
8158   PetscCall(PetscSortIntWithArray(depth+1, starts, depths));
8159   PetscCall(MPIU_Allreduce(depths, gdepths, depth+1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject) dm)));
8160   for (d = 0; d <= depth; ++d) {
8161     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]);
8162   }
8163   for (d = 0; d <= depth; ++d) {
8164     PetscInt pStart, pEnd, gsize;
8165 
8166     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8167     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8168     shift += gsize;
8169   }
8170   PetscCall(ISConcatenate(PetscObjectComm((PetscObject) dm), depth+1, nums, globalPointNumbers));
8171   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8172   PetscFunctionReturn(0);
8173 }
8174 
8175 /*@
8176   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8177 
8178   Input Parameter:
8179 . dm - The DMPlex object
8180 
8181   Output Parameter:
8182 . ranks - The rank field
8183 
8184   Options Database Keys:
8185 . -dm_partition_view - Adds the rank field into the DM output from -dm_view using the same viewer
8186 
8187   Level: intermediate
8188 
8189 .seealso: `DMView()`
8190 @*/
8191 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8192 {
8193   DM             rdm;
8194   PetscFE        fe;
8195   PetscScalar   *r;
8196   PetscMPIInt    rank;
8197   DMPolytopeType ct;
8198   PetscInt       dim, cStart, cEnd, c;
8199   PetscBool      simplex;
8200 
8201   PetscFunctionBeginUser;
8202   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8203   PetscValidPointer(ranks, 2);
8204   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject) dm), &rank));
8205   PetscCall(DMClone(dm, &rdm));
8206   PetscCall(DMGetDimension(rdm, &dim));
8207   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8208   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8209   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct)+1 ? PETSC_TRUE : PETSC_FALSE;
8210   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8211   PetscCall(PetscObjectSetName((PetscObject) fe, "rank"));
8212   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject) fe));
8213   PetscCall(PetscFEDestroy(&fe));
8214   PetscCall(DMCreateDS(rdm));
8215   PetscCall(DMCreateGlobalVector(rdm, ranks));
8216   PetscCall(PetscObjectSetName((PetscObject) *ranks, "partition"));
8217   PetscCall(VecGetArray(*ranks, &r));
8218   for (c = cStart; c < cEnd; ++c) {
8219     PetscScalar *lr;
8220 
8221     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8222     if (lr) *lr = rank;
8223   }
8224   PetscCall(VecRestoreArray(*ranks, &r));
8225   PetscCall(DMDestroy(&rdm));
8226   PetscFunctionReturn(0);
8227 }
8228 
8229 /*@
8230   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8231 
8232   Input Parameters:
8233 + dm    - The DMPlex
8234 - label - The DMLabel
8235 
8236   Output Parameter:
8237 . val - The label value field
8238 
8239   Options Database Keys:
8240 . -dm_label_view - Adds the label value field into the DM output from -dm_view using the same viewer
8241 
8242   Level: intermediate
8243 
8244 .seealso: `DMView()`
8245 @*/
8246 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8247 {
8248   DM             rdm;
8249   PetscFE        fe;
8250   PetscScalar   *v;
8251   PetscInt       dim, cStart, cEnd, c;
8252 
8253   PetscFunctionBeginUser;
8254   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8255   PetscValidPointer(label, 2);
8256   PetscValidPointer(val, 3);
8257   PetscCall(DMClone(dm, &rdm));
8258   PetscCall(DMGetDimension(rdm, &dim));
8259   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject) rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8260   PetscCall(PetscObjectSetName((PetscObject) fe, "label_value"));
8261   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject) fe));
8262   PetscCall(PetscFEDestroy(&fe));
8263   PetscCall(DMCreateDS(rdm));
8264   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8265   PetscCall(DMCreateGlobalVector(rdm, val));
8266   PetscCall(PetscObjectSetName((PetscObject) *val, "label_value"));
8267   PetscCall(VecGetArray(*val, &v));
8268   for (c = cStart; c < cEnd; ++c) {
8269     PetscScalar *lv;
8270     PetscInt     cval;
8271 
8272     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8273     PetscCall(DMLabelGetValue(label, c, &cval));
8274     *lv = cval;
8275   }
8276   PetscCall(VecRestoreArray(*val, &v));
8277   PetscCall(DMDestroy(&rdm));
8278   PetscFunctionReturn(0);
8279 }
8280 
8281 /*@
8282   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8283 
8284   Input Parameter:
8285 . dm - The DMPlex object
8286 
8287   Notes:
8288   This is a useful diagnostic when creating meshes programmatically.
8289 
8290   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8291 
8292   Level: developer
8293 
8294 .seealso: `DMCreate()`, `DMSetFromOptions()`
8295 @*/
8296 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8297 {
8298   PetscSection    coneSection, supportSection;
8299   const PetscInt *cone, *support;
8300   PetscInt        coneSize, c, supportSize, s;
8301   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8302   PetscBool       storagecheck = PETSC_TRUE;
8303 
8304   PetscFunctionBegin;
8305   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8306   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8307   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8308   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8309   /* Check that point p is found in the support of its cone points, and vice versa */
8310   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8311   for (p = pStart; p < pEnd; ++p) {
8312     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8313     PetscCall(DMPlexGetCone(dm, p, &cone));
8314     for (c = 0; c < coneSize; ++c) {
8315       PetscBool dup = PETSC_FALSE;
8316       PetscInt  d;
8317       for (d = c-1; d >= 0; --d) {
8318         if (cone[c] == cone[d]) {dup = PETSC_TRUE; break;}
8319       }
8320       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8321       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8322       for (s = 0; s < supportSize; ++s) {
8323         if (support[s] == p) break;
8324       }
8325       if ((s >= supportSize) || (dup && (support[s+1] != p))) {
8326         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8327         for (s = 0; s < coneSize; ++s) {
8328           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8329         }
8330         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8331         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8332         for (s = 0; s < supportSize; ++s) {
8333           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8334         }
8335         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8336         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]);
8337         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8338       }
8339     }
8340     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8341     if (p != pp) { storagecheck = PETSC_FALSE; continue; }
8342     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8343     PetscCall(DMPlexGetSupport(dm, p, &support));
8344     for (s = 0; s < supportSize; ++s) {
8345       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8346       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8347       for (c = 0; c < coneSize; ++c) {
8348         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8349         if (cone[c] != pp) { c = 0; break; }
8350         if (cone[c] == p) break;
8351       }
8352       if (c >= coneSize) {
8353         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8354         for (c = 0; c < supportSize; ++c) {
8355           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8356         }
8357         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8358         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8359         for (c = 0; c < coneSize; ++c) {
8360           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8361         }
8362         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8363         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8364       }
8365     }
8366   }
8367   if (storagecheck) {
8368     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8369     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8370     PetscCheck(csize == ssize,PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8371   }
8372   PetscFunctionReturn(0);
8373 }
8374 
8375 /*
8376   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.
8377 */
8378 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
8379 {
8380   DMPolytopeType  cct;
8381   PetscInt        ptpoints[4];
8382   const PetscInt *cone, *ccone, *ptcone;
8383   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8384 
8385   PetscFunctionBegin;
8386   *unsplit = 0;
8387   switch (ct) {
8388     case DM_POLYTOPE_POINT_PRISM_TENSOR:
8389       ptpoints[npt++] = c;
8390       break;
8391     case DM_POLYTOPE_SEG_PRISM_TENSOR:
8392       PetscCall(DMPlexGetCone(dm, c, &cone));
8393       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8394       for (cp = 0; cp < coneSize; ++cp) {
8395         PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8396         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8397       }
8398       break;
8399     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8400     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8401       PetscCall(DMPlexGetCone(dm, c, &cone));
8402       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8403       for (cp = 0; cp < coneSize; ++cp) {
8404         PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8405         PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8406         for (ccp = 0; ccp < cconeSize; ++ccp) {
8407           PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8408           if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8409             PetscInt p;
8410             for (p = 0; p < npt; ++p) if (ptpoints[p] == ccone[ccp]) break;
8411             if (p == npt) ptpoints[npt++] = ccone[ccp];
8412           }
8413         }
8414       }
8415       break;
8416     default: break;
8417   }
8418   for (pt = 0; pt < npt; ++pt) {
8419     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8420     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8421   }
8422   PetscFunctionReturn(0);
8423 }
8424 
8425 /*@
8426   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8427 
8428   Input Parameters:
8429 + dm - The DMPlex object
8430 - cellHeight - Normally 0
8431 
8432   Notes:
8433   This is a useful diagnostic when creating meshes programmatically.
8434   Currently applicable only to homogeneous simplex or tensor meshes.
8435 
8436   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8437 
8438   Level: developer
8439 
8440 .seealso: `DMCreate()`, `DMSetFromOptions()`
8441 @*/
8442 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
8443 {
8444   DMPlexInterpolatedFlag interp;
8445   DMPolytopeType         ct;
8446   PetscInt               vStart, vEnd, cStart, cEnd, c;
8447 
8448   PetscFunctionBegin;
8449   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8450   PetscCall(DMPlexIsInterpolated(dm, &interp));
8451   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8452   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8453   for (c = cStart; c < cEnd; ++c) {
8454     PetscInt *closure = NULL;
8455     PetscInt  coneSize, closureSize, cl, Nv = 0;
8456 
8457     PetscCall(DMPlexGetCellType(dm, c, &ct));
8458     PetscCheck((PetscInt) ct >= 0,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
8459     if (ct == DM_POLYTOPE_UNKNOWN) continue;
8460     if (interp == DMPLEX_INTERPOLATED_FULL) {
8461       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8462       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));
8463     }
8464     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8465     for (cl = 0; cl < closureSize*2; cl += 2) {
8466       const PetscInt p = closure[cl];
8467       if ((p >= vStart) && (p < vEnd)) ++Nv;
8468     }
8469     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8470     /* Special Case: Tensor faces with identified vertices */
8471     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
8472       PetscInt unsplit;
8473 
8474       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8475       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
8476     }
8477     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));
8478   }
8479   PetscFunctionReturn(0);
8480 }
8481 
8482 /*@
8483   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
8484 
8485   Collective
8486 
8487   Input Parameters:
8488 + dm - The DMPlex object
8489 - cellHeight - Normally 0
8490 
8491   Notes:
8492   This is a useful diagnostic when creating meshes programmatically.
8493   This routine is only relevant for meshes that are fully interpolated across all ranks.
8494   It will error out if a partially interpolated mesh is given on some rank.
8495   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
8496 
8497   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8498 
8499   Level: developer
8500 
8501 .seealso: `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
8502 @*/
8503 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
8504 {
8505   PetscInt       dim, depth, vStart, vEnd, cStart, cEnd, c, h;
8506   DMPlexInterpolatedFlag interpEnum;
8507 
8508   PetscFunctionBegin;
8509   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8510   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
8511   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(0);
8512   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
8513     PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported");
8514     PetscFunctionReturn(0);
8515   }
8516 
8517   PetscCall(DMGetDimension(dm, &dim));
8518   PetscCall(DMPlexGetDepth(dm, &depth));
8519   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8520   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
8521     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
8522     for (c = cStart; c < cEnd; ++c) {
8523       const PetscInt      *cone, *ornt, *faceSizes, *faces;
8524       const DMPolytopeType *faceTypes;
8525       DMPolytopeType        ct;
8526       PetscInt              numFaces, coneSize, f;
8527       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
8528 
8529       PetscCall(DMPlexGetCellType(dm, c, &ct));
8530       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8531       if (unsplit) continue;
8532       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8533       PetscCall(DMPlexGetCone(dm, c, &cone));
8534       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
8535       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8536       for (cl = 0; cl < closureSize*2; cl += 2) {
8537         const PetscInt p = closure[cl];
8538         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
8539       }
8540       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8541       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);
8542       for (f = 0; f < numFaces; ++f) {
8543         DMPolytopeType fct;
8544         PetscInt       *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
8545 
8546         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
8547         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
8548         for (cl = 0; cl < fclosureSize*2; cl += 2) {
8549           const PetscInt p = fclosure[cl];
8550           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
8551         }
8552         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]);
8553         for (v = 0; v < fnumCorners; ++v) {
8554           if (fclosure[v] != faces[fOff+v]) {
8555             PetscInt v1;
8556 
8557             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
8558             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
8559             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
8560             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff+v1]));
8561             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8562             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]);
8563           }
8564         }
8565         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
8566         fOff += faceSizes[f];
8567       }
8568       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8569       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8570     }
8571   }
8572   PetscFunctionReturn(0);
8573 }
8574 
8575 /*@
8576   DMPlexCheckGeometry - Check the geometry of mesh cells
8577 
8578   Input Parameter:
8579 . dm - The DMPlex object
8580 
8581   Notes:
8582   This is a useful diagnostic when creating meshes programmatically.
8583 
8584   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8585 
8586   Level: developer
8587 
8588 .seealso: `DMCreate()`, `DMSetFromOptions()`
8589 @*/
8590 PetscErrorCode DMPlexCheckGeometry(DM dm)
8591 {
8592   Vec       coordinates;
8593   PetscReal detJ, J[9], refVol = 1.0;
8594   PetscReal vol;
8595   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
8596 
8597   PetscFunctionBegin;
8598   PetscCall(DMGetDimension(dm, &dim));
8599   PetscCall(DMGetCoordinateDim(dm, &dE));
8600   if (dim != dE) PetscFunctionReturn(0);
8601   PetscCall(DMPlexGetDepth(dm, &depth));
8602   for (d = 0; d < dim; ++d) refVol *= 2.0;
8603   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
8604   /* Make sure local coordinates are created, because that step is collective */
8605   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
8606   for (c = cStart; c < cEnd; ++c) {
8607     DMPolytopeType ct;
8608     PetscInt       unsplit;
8609     PetscBool      ignoreZeroVol = PETSC_FALSE;
8610 
8611     PetscCall(DMPlexGetCellType(dm, c, &ct));
8612     switch (ct) {
8613       case DM_POLYTOPE_SEG_PRISM_TENSOR:
8614       case DM_POLYTOPE_TRI_PRISM_TENSOR:
8615       case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8616         ignoreZeroVol = PETSC_TRUE; break;
8617       default: break;
8618     }
8619     switch (ct) {
8620       case DM_POLYTOPE_TRI_PRISM:
8621       case DM_POLYTOPE_TRI_PRISM_TENSOR:
8622       case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8623       case DM_POLYTOPE_PYRAMID:
8624         continue;
8625       default: break;
8626     }
8627     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8628     if (unsplit) continue;
8629     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
8630     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);
8631     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ*refVol)));
8632     /* This should work with periodicity since DG coordinates should be used */
8633     if (depth > 1) {
8634       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
8635       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);
8636       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double) vol));
8637     }
8638   }
8639   PetscFunctionReturn(0);
8640 }
8641 
8642 /*@
8643   DMPlexCheckPointSF - Check that several necessary conditions are met for the Point SF of this plex.
8644 
8645   Collective
8646 
8647   Input Parameters:
8648 + dm - The DMPlex object
8649 - pointSF - The Point SF, or NULL for Point SF attached to DM
8650 
8651   Notes:
8652   This is mainly intended for debugging/testing purposes.
8653 
8654   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8655 
8656   Level: developer
8657 
8658 .seealso: `DMGetPointSF()`, `DMSetFromOptions()`
8659 @*/
8660 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF)
8661 {
8662   PetscInt        l, nleaves, nroots, overlap;
8663   const PetscInt *locals;
8664   const PetscSFNode *remotes;
8665   PetscBool       distributed;
8666   MPI_Comm        comm;
8667   PetscMPIInt     rank;
8668 
8669   PetscFunctionBegin;
8670   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8671   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
8672   else         pointSF = dm->sf;
8673   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
8674   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
8675   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8676   {
8677     PetscMPIInt    mpiFlag;
8678 
8679     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF),&mpiFlag));
8680     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)",mpiFlag);
8681   }
8682   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
8683   PetscCall(DMPlexIsDistributed(dm, &distributed));
8684   if (!distributed) {
8685     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);
8686     PetscFunctionReturn(0);
8687   }
8688   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);
8689   PetscCall(DMPlexGetOverlap(dm, &overlap));
8690 
8691   /* Check SF graph is compatible with DMPlex chart */
8692   {
8693     PetscInt pStart, pEnd, maxLeaf;
8694 
8695     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8696     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
8697     PetscCheck(pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd-pStart, nroots);
8698     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
8699   }
8700 
8701   /* Check Point SF has no local points referenced */
8702   for (l = 0; l < nleaves; l++) {
8703     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);
8704   }
8705 
8706   /* Check there are no cells in interface */
8707   if (!overlap) {
8708     PetscInt cellHeight, cStart, cEnd;
8709 
8710     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8711     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8712     for (l = 0; l < nleaves; ++l) {
8713       const PetscInt point = locals ? locals[l] : l;
8714 
8715       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
8716     }
8717   }
8718 
8719   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
8720   {
8721     const PetscInt *rootdegree;
8722 
8723     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
8724     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
8725     for (l = 0; l < nleaves; ++l) {
8726       const PetscInt  point = locals ? locals[l] : l;
8727       const PetscInt *cone;
8728       PetscInt        coneSize, c, idx;
8729 
8730       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
8731       PetscCall(DMPlexGetCone(dm, point, &cone));
8732       for (c = 0; c < coneSize; ++c) {
8733         if (!rootdegree[cone[c]]) {
8734           if (locals) {
8735             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
8736           } else {
8737             idx = (cone[c] < nleaves) ? cone[c] : -1;
8738           }
8739           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
8740         }
8741       }
8742     }
8743   }
8744   PetscFunctionReturn(0);
8745 }
8746 
8747 /*@
8748   DMPlexCheck - Perform various checks of Plex sanity
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   Currently does not include DMPlexCheckCellShape().
8759 
8760   Level: developer
8761 
8762 .seealso: DMCreate(), DMSetFromOptions()
8763 @*/
8764 PetscErrorCode DMPlexCheck(DM dm)
8765 {
8766   PetscInt cellHeight;
8767 
8768   PetscFunctionBegin;
8769   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8770   PetscCall(DMPlexCheckSymmetry(dm));
8771   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
8772   PetscCall(DMPlexCheckFaces(dm, cellHeight));
8773   PetscCall(DMPlexCheckGeometry(dm));
8774   PetscCall(DMPlexCheckPointSF(dm, NULL));
8775   PetscCall(DMPlexCheckInterfaceCones(dm));
8776   PetscFunctionReturn(0);
8777 }
8778 
8779 typedef struct cell_stats
8780 {
8781   PetscReal min, max, sum, squaresum;
8782   PetscInt  count;
8783 } cell_stats_t;
8784 
8785 static void MPIAPI cell_stats_reduce(void *a, void *b, int * len, MPI_Datatype *datatype)
8786 {
8787   PetscInt i, N = *len;
8788 
8789   for (i = 0; i < N; i++) {
8790     cell_stats_t *A = (cell_stats_t *) a;
8791     cell_stats_t *B = (cell_stats_t *) b;
8792 
8793     B->min = PetscMin(A->min,B->min);
8794     B->max = PetscMax(A->max,B->max);
8795     B->sum += A->sum;
8796     B->squaresum += A->squaresum;
8797     B->count += A->count;
8798   }
8799 }
8800 
8801 /*@
8802   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
8803 
8804   Collective on dm
8805 
8806   Input Parameters:
8807 + dm        - The DMPlex object
8808 . output    - If true, statistics will be displayed on stdout
8809 - condLimit - Display all cells above this condition number, or PETSC_DETERMINE for no cell output
8810 
8811   Notes:
8812   This is mainly intended for debugging/testing purposes.
8813 
8814   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8815 
8816   Level: developer
8817 
8818 .seealso: `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
8819 @*/
8820 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
8821 {
8822   DM             dmCoarse;
8823   cell_stats_t   stats, globalStats;
8824   MPI_Comm       comm = PetscObjectComm((PetscObject)dm);
8825   PetscReal      *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
8826   PetscReal      limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
8827   PetscInt       cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
8828   PetscMPIInt    rank,size;
8829 
8830   PetscFunctionBegin;
8831   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8832   stats.min   = PETSC_MAX_REAL;
8833   stats.max   = PETSC_MIN_REAL;
8834   stats.sum   = stats.squaresum = 0.;
8835   stats.count = 0;
8836 
8837   PetscCallMPI(MPI_Comm_size(comm, &size));
8838   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8839   PetscCall(DMGetCoordinateDim(dm,&cdim));
8840   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
8841   PetscCall(DMPlexGetSimplexOrBoxCells(dm,0,&cStart,&cEnd));
8842   PetscCall(DMPlexGetDepthStratum(dm,1,&eStart,&eEnd));
8843   for (c = cStart; c < cEnd; c++) {
8844     PetscInt  i;
8845     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
8846 
8847     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm,c,NULL,J,invJ,&detJ));
8848     PetscCheck(detJ >= 0.0,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
8849     for (i = 0; i < PetscSqr(cdim); ++i) {
8850       frobJ    += J[i] * J[i];
8851       frobInvJ += invJ[i] * invJ[i];
8852     }
8853     cond2 = frobJ * frobInvJ;
8854     cond  = PetscSqrtReal(cond2);
8855 
8856     stats.min        = PetscMin(stats.min,cond);
8857     stats.max        = PetscMax(stats.max,cond);
8858     stats.sum       += cond;
8859     stats.squaresum += cond2;
8860     stats.count++;
8861     if (output && cond > limit) {
8862       PetscSection coordSection;
8863       Vec          coordsLocal;
8864       PetscScalar *coords = NULL;
8865       PetscInt     Nv, d, clSize, cl, *closure = NULL;
8866 
8867       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
8868       PetscCall(DMGetCoordinateSection(dm, &coordSection));
8869       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
8870       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double) cond));
8871       for (i = 0; i < Nv/cdim; ++i) {
8872         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
8873         for (d = 0; d < cdim; ++d) {
8874           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
8875           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double) PetscRealPart(coords[i*cdim+d])));
8876         }
8877         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
8878       }
8879       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
8880       for (cl = 0; cl < clSize*2; cl += 2) {
8881         const PetscInt edge = closure[cl];
8882 
8883         if ((edge >= eStart) && (edge < eEnd)) {
8884           PetscReal len;
8885 
8886           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
8887           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double) len));
8888         }
8889       }
8890       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
8891       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
8892     }
8893   }
8894   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
8895 
8896   if (size > 1) {
8897     PetscMPIInt   blockLengths[2] = {4,1};
8898     MPI_Aint      blockOffsets[2] = {offsetof(cell_stats_t,min),offsetof(cell_stats_t,count)};
8899     MPI_Datatype  blockTypes[2]   = {MPIU_REAL,MPIU_INT}, statType;
8900     MPI_Op        statReduce;
8901 
8902     PetscCallMPI(MPI_Type_create_struct(2,blockLengths,blockOffsets,blockTypes,&statType));
8903     PetscCallMPI(MPI_Type_commit(&statType));
8904     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
8905     PetscCallMPI(MPI_Reduce(&stats,&globalStats,1,statType,statReduce,0,comm));
8906     PetscCallMPI(MPI_Op_free(&statReduce));
8907     PetscCallMPI(MPI_Type_free(&statType));
8908   } else {
8909     PetscCall(PetscArraycpy(&globalStats,&stats,1));
8910   }
8911   if (rank == 0) {
8912     count = globalStats.count;
8913     min   = globalStats.min;
8914     max   = globalStats.max;
8915     mean  = globalStats.sum / globalStats.count;
8916     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1),0)) : 0.0;
8917   }
8918 
8919   if (output) {
8920     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));
8921   }
8922   PetscCall(PetscFree2(J,invJ));
8923 
8924   PetscCall(DMGetCoarseDM(dm,&dmCoarse));
8925   if (dmCoarse) {
8926     PetscBool isplex;
8927 
8928     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse,DMPLEX,&isplex));
8929     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse,output,condLimit));
8930   }
8931   PetscFunctionReturn(0);
8932 }
8933 
8934 /*@
8935   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
8936   orthogonal quality below given tolerance.
8937 
8938   Collective on dm
8939 
8940   Input Parameters:
8941 + dm   - The DMPlex object
8942 . fv   - Optional PetscFV object for pre-computed cell/face centroid information
8943 - atol - [0, 1] Absolute tolerance for tagging cells.
8944 
8945   Output Parameters:
8946 + OrthQual      - Vec containing orthogonal quality per cell
8947 - OrthQualLabel - DMLabel tagging cells below atol with DM_ADAPT_REFINE
8948 
8949   Options Database Keys:
8950 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only PETSCVIEWERASCII is
8951 supported.
8952 - -dm_plex_orthogonal_quality_vec_view - view OrthQual vector.
8953 
8954   Notes:
8955   Orthogonal quality is given by the following formula:
8956 
8957   \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]
8958 
8959   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
8960   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
8961   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
8962   calculating the cosine of the angle between these vectors.
8963 
8964   Orthogonal quality ranges from 1 (best) to 0 (worst).
8965 
8966   This routine is mainly useful for FVM, however is not restricted to only FVM. The PetscFV object is optionally used to check for
8967   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
8968 
8969   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
8970 
8971   Level: intermediate
8972 
8973 .seealso: `DMPlexCheckCellShape()`, `DMCreateLabel()`
8974 @*/
8975 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
8976 {
8977   PetscInt                nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
8978   PetscInt                *idx;
8979   PetscScalar             *oqVals;
8980   const PetscScalar       *cellGeomArr, *faceGeomArr;
8981   PetscReal               *ci, *fi, *Ai;
8982   MPI_Comm                comm;
8983   Vec                     cellgeom, facegeom;
8984   DM                      dmFace, dmCell;
8985   IS                      glob;
8986   ISLocalToGlobalMapping  ltog;
8987   PetscViewer             vwr;
8988 
8989   PetscFunctionBegin;
8990   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8991   if (fv) {PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);}
8992   PetscValidPointer(OrthQual, 4);
8993   PetscCheck(atol >= 0.0 && atol <= 1.0,PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Absolute tolerance %g not in [0,1]",(double)atol);
8994   PetscCall(PetscObjectGetComm((PetscObject) dm, &comm));
8995   PetscCall(DMGetDimension(dm, &nc));
8996   PetscCheck(nc >= 2,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
8997   {
8998     DMPlexInterpolatedFlag interpFlag;
8999 
9000     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9001     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9002       PetscMPIInt rank;
9003 
9004       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9005       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9006     }
9007   }
9008   if (OrthQualLabel) {
9009     PetscValidPointer(OrthQualLabel, 5);
9010     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9011     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9012   } else {*OrthQualLabel = NULL;}
9013   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9014   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9015   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9016   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9017   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9018   PetscCall(VecCreate(comm, OrthQual));
9019   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9020   PetscCall(VecSetSizes(*OrthQual, cEnd-cStart, PETSC_DETERMINE));
9021   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9022   PetscCall(VecSetUp(*OrthQual));
9023   PetscCall(ISDestroy(&glob));
9024   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9025   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9026   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9027   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9028   PetscCall(VecGetDM(cellgeom, &dmCell));
9029   PetscCall(VecGetDM(facegeom, &dmFace));
9030   PetscCall(PetscMalloc5(cEnd-cStart, &idx, cEnd-cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9031   for (cell = cStart; cell < cEnd; cellIter++,cell++) {
9032     PetscInt           cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9033     PetscInt           cellarr[2], *adj = NULL;
9034     PetscScalar        *cArr, *fArr;
9035     PetscReal          minvalc = 1.0, minvalf = 1.0;
9036     PetscFVCellGeom    *cg;
9037 
9038     idx[cellIter] = cell-cStart;
9039     cellarr[0] = cell;
9040     /* Make indexing into cellGeom easier */
9041     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9042     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9043     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9044     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9045     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++,cellneigh++) {
9046       PetscInt         i;
9047       const PetscInt   neigh = adj[cellneigh];
9048       PetscReal        normci = 0, normfi = 0, normai = 0;
9049       PetscFVCellGeom  *cgneigh;
9050       PetscFVFaceGeom  *fg;
9051 
9052       /* Don't count ourselves in the neighbor list */
9053       if (neigh == cell) continue;
9054       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9055       cellarr[1] = neigh;
9056       {
9057         PetscInt       numcovpts;
9058         const PetscInt *covpts;
9059 
9060         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9061         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9062         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9063       }
9064 
9065       /* Compute c_i, f_i and their norms */
9066       for (i = 0; i < nc; i++) {
9067         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9068         fi[i] = fg->centroid[i] - cg->centroid[i];
9069         Ai[i] = fg->normal[i];
9070         normci += PetscPowReal(ci[i], 2);
9071         normfi += PetscPowReal(fi[i], 2);
9072         normai += PetscPowReal(Ai[i], 2);
9073       }
9074       normci = PetscSqrtReal(normci);
9075       normfi = PetscSqrtReal(normfi);
9076       normai = PetscSqrtReal(normai);
9077 
9078       /* Normalize and compute for each face-cell-normal pair */
9079       for (i = 0; i < nc; i++) {
9080         ci[i] = ci[i]/normci;
9081         fi[i] = fi[i]/normfi;
9082         Ai[i] = Ai[i]/normai;
9083         /* PetscAbs because I don't know if normals are guaranteed to point out */
9084         cArr[cellneighiter] += PetscAbs(Ai[i]*ci[i]);
9085         fArr[cellneighiter] += PetscAbs(Ai[i]*fi[i]);
9086       }
9087       if (PetscRealPart(cArr[cellneighiter]) < minvalc) {
9088         minvalc = PetscRealPart(cArr[cellneighiter]);
9089       }
9090       if (PetscRealPart(fArr[cellneighiter]) < minvalf) {
9091         minvalf = PetscRealPart(fArr[cellneighiter]);
9092       }
9093     }
9094     PetscCall(PetscFree(adj));
9095     PetscCall(PetscFree2(cArr, fArr));
9096     /* Defer to cell if they're equal */
9097     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9098     if (OrthQualLabel) {
9099       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9100     }
9101   }
9102   PetscCall(VecSetValuesLocal(*OrthQual, cEnd-cStart, idx, oqVals, INSERT_VALUES));
9103   PetscCall(VecAssemblyBegin(*OrthQual));
9104   PetscCall(VecAssemblyEnd(*OrthQual));
9105   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9106   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9107   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9108   if (OrthQualLabel) {
9109     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9110   }
9111   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9112   PetscCall(PetscViewerDestroy(&vwr));
9113   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9114   PetscFunctionReturn(0);
9115 }
9116 
9117 /* this is here insead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9118  * interpolator construction */
9119 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9120 {
9121   PetscSection   section, newSection, gsection;
9122   PetscSF        sf;
9123   PetscBool      hasConstraints, ghasConstraints;
9124 
9125   PetscFunctionBegin;
9126   PetscValidHeaderSpecific(dm,DM_CLASSID,1);
9127   PetscValidPointer(odm,2);
9128   PetscCall(DMGetLocalSection(dm, &section));
9129   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9130   PetscCallMPI(MPI_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject) dm)));
9131   if (!ghasConstraints) {
9132     PetscCall(PetscObjectReference((PetscObject)dm));
9133     *odm = dm;
9134     PetscFunctionReturn(0);
9135   }
9136   PetscCall(DMClone(dm, odm));
9137   PetscCall(DMCopyFields(dm, *odm));
9138   PetscCall(DMGetLocalSection(*odm, &newSection));
9139   PetscCall(DMGetPointSF(*odm, &sf));
9140   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9141   PetscCall(DMSetGlobalSection(*odm, gsection));
9142   PetscCall(PetscSectionDestroy(&gsection));
9143   PetscFunctionReturn(0);
9144 }
9145 
9146 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9147 {
9148   DM             dmco, dmfo;
9149   Mat            interpo;
9150   Vec            rscale;
9151   Vec            cglobalo, clocal;
9152   Vec            fglobal, fglobalo, flocal;
9153   PetscBool      regular;
9154 
9155   PetscFunctionBegin;
9156   PetscCall(DMGetFullDM(dmc, &dmco));
9157   PetscCall(DMGetFullDM(dmf, &dmfo));
9158   PetscCall(DMSetCoarseDM(dmfo, dmco));
9159   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9160   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9161   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9162   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9163   PetscCall(DMCreateLocalVector(dmc, &clocal));
9164   PetscCall(VecSet(cglobalo, 0.));
9165   PetscCall(VecSet(clocal, 0.));
9166   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9167   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9168   PetscCall(DMCreateLocalVector(dmf, &flocal));
9169   PetscCall(VecSet(fglobal, 0.));
9170   PetscCall(VecSet(fglobalo, 0.));
9171   PetscCall(VecSet(flocal, 0.));
9172   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9173   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9174   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9175   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9176   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9177   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9178   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9179   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9180   *shift = fglobal;
9181   PetscCall(VecDestroy(&flocal));
9182   PetscCall(VecDestroy(&fglobalo));
9183   PetscCall(VecDestroy(&clocal));
9184   PetscCall(VecDestroy(&cglobalo));
9185   PetscCall(VecDestroy(&rscale));
9186   PetscCall(MatDestroy(&interpo));
9187   PetscCall(DMDestroy(&dmfo));
9188   PetscCall(DMDestroy(&dmco));
9189   PetscFunctionReturn(0);
9190 }
9191 
9192 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9193 {
9194   PetscObject    shifto;
9195   Vec            shift;
9196 
9197   PetscFunctionBegin;
9198   if (!interp) {
9199     Vec rscale;
9200 
9201     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9202     PetscCall(VecDestroy(&rscale));
9203   } else {
9204     PetscCall(PetscObjectReference((PetscObject)interp));
9205   }
9206   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9207   if (!shifto) {
9208     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9209     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject) shift));
9210     shifto = (PetscObject) shift;
9211     PetscCall(VecDestroy(&shift));
9212   }
9213   shift = (Vec) shifto;
9214   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9215   PetscCall(VecAXPY(fineSol, 1.0, shift));
9216   PetscCall(MatDestroy(&interp));
9217   PetscFunctionReturn(0);
9218 }
9219 
9220 /* Pointwise interpolation
9221      Just code FEM for now
9222      u^f = I u^c
9223      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9224      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9225      I_{ij} = psi^f_i phi^c_j
9226 */
9227 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9228 {
9229   PetscSection   gsc, gsf;
9230   PetscInt       m, n;
9231   void          *ctx;
9232   DM             cdm;
9233   PetscBool      regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9234 
9235   PetscFunctionBegin;
9236   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9237   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9238   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9239   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9240 
9241   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9242   PetscCall(MatCreate(PetscObjectComm((PetscObject) dmCoarse), interpolation));
9243   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9244   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9245   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9246 
9247   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9248   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9249   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9250   else                                            PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9251   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9252   if (scaling) {
9253     /* Use naive scaling */
9254     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9255   }
9256   PetscFunctionReturn(0);
9257 }
9258 
9259 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9260 {
9261   VecScatter     ctx;
9262 
9263   PetscFunctionBegin;
9264   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9265   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9266   PetscCall(VecScatterDestroy(&ctx));
9267   PetscFunctionReturn(0);
9268 }
9269 
9270 static void g0_identity_private(PetscInt dim, PetscInt Nf, PetscInt NfAux,
9271                                 const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[],
9272                                 const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[],
9273                                 PetscReal t, PetscReal u_tShift, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar g0[])
9274 {
9275   const PetscInt Nc = uOff[1] - uOff[0];
9276   PetscInt       c;
9277   for (c = 0; c < Nc; ++c) g0[c*Nc+c] = 1.0;
9278 }
9279 
9280 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9281 {
9282   DM             dmc;
9283   PetscDS        ds;
9284   Vec            ones, locmass;
9285   IS             cellIS;
9286   PetscFormKey   key;
9287   PetscInt       depth;
9288 
9289   PetscFunctionBegin;
9290   PetscCall(DMClone(dm, &dmc));
9291   PetscCall(DMCopyDisc(dm, dmc));
9292   PetscCall(DMGetDS(dmc, &ds));
9293   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9294   PetscCall(DMCreateGlobalVector(dmc, mass));
9295   PetscCall(DMGetLocalVector(dmc, &ones));
9296   PetscCall(DMGetLocalVector(dmc, &locmass));
9297   PetscCall(DMPlexGetDepth(dmc, &depth));
9298   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9299   PetscCall(VecSet(locmass, 0.0));
9300   PetscCall(VecSet(ones, 1.0));
9301   key.label = NULL;
9302   key.value = 0;
9303   key.field = 0;
9304   key.part  = 0;
9305   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9306   PetscCall(ISDestroy(&cellIS));
9307   PetscCall(VecSet(*mass, 0.0));
9308   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9309   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9310   PetscCall(DMRestoreLocalVector(dmc, &ones));
9311   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9312   PetscCall(DMDestroy(&dmc));
9313   PetscFunctionReturn(0);
9314 }
9315 
9316 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9317 {
9318   PetscSection   gsc, gsf;
9319   PetscInt       m, n;
9320   void          *ctx;
9321   DM             cdm;
9322   PetscBool      regular;
9323 
9324   PetscFunctionBegin;
9325   if (dmFine == dmCoarse) {
9326     DM            dmc;
9327     PetscDS       ds;
9328     PetscWeakForm wf;
9329     Vec           u;
9330     IS            cellIS;
9331     PetscFormKey  key;
9332     PetscInt      depth;
9333 
9334     PetscCall(DMClone(dmFine, &dmc));
9335     PetscCall(DMCopyDisc(dmFine, dmc));
9336     PetscCall(DMGetDS(dmc, &ds));
9337     PetscCall(PetscDSGetWeakForm(ds, &wf));
9338     PetscCall(PetscWeakFormClear(wf));
9339     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9340     PetscCall(DMCreateMatrix(dmc, mass));
9341     PetscCall(DMGetLocalVector(dmc, &u));
9342     PetscCall(DMPlexGetDepth(dmc, &depth));
9343     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9344     PetscCall(MatZeroEntries(*mass));
9345     key.label = NULL;
9346     key.value = 0;
9347     key.field = 0;
9348     key.part  = 0;
9349     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9350     PetscCall(ISDestroy(&cellIS));
9351     PetscCall(DMRestoreLocalVector(dmc, &u));
9352     PetscCall(DMDestroy(&dmc));
9353   } else {
9354     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9355     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9356     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9357     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9358 
9359     PetscCall(MatCreate(PetscObjectComm((PetscObject) dmCoarse), mass));
9360     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9361     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9362     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9363 
9364     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9365     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9366     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9367     else                            PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9368   }
9369   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9370   PetscFunctionReturn(0);
9371 }
9372 
9373 /*@
9374   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9375 
9376   Input Parameter:
9377 . dm - The DMPlex object
9378 
9379   Output Parameter:
9380 . regular - The flag
9381 
9382   Level: intermediate
9383 
9384 .seealso: `DMPlexSetRegularRefinement()`
9385 @*/
9386 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
9387 {
9388   PetscFunctionBegin;
9389   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9390   PetscValidBoolPointer(regular, 2);
9391   *regular = ((DM_Plex *) dm->data)->regularRefinement;
9392   PetscFunctionReturn(0);
9393 }
9394 
9395 /*@
9396   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9397 
9398   Input Parameters:
9399 + dm - The DMPlex object
9400 - regular - The flag
9401 
9402   Level: intermediate
9403 
9404 .seealso: `DMPlexGetRegularRefinement()`
9405 @*/
9406 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
9407 {
9408   PetscFunctionBegin;
9409   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9410   ((DM_Plex *) dm->data)->regularRefinement = regular;
9411   PetscFunctionReturn(0);
9412 }
9413 
9414 /* anchors */
9415 /*@
9416   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9417   call DMPlexGetAnchors() directly: if there are anchors, then DMPlexGetAnchors() is called during DMGetDefaultConstraints().
9418 
9419   not collective
9420 
9421   Input Parameter:
9422 . dm - The DMPlex object
9423 
9424   Output Parameters:
9425 + anchorSection - If not NULL, set to the section describing which points anchor the constrained points.
9426 - anchorIS - If not NULL, set to the list of anchors indexed by anchorSection
9427 
9428   Level: intermediate
9429 
9430 .seealso: `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9431 @*/
9432 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
9433 {
9434   DM_Plex *plex = (DM_Plex *)dm->data;
9435 
9436   PetscFunctionBegin;
9437   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9438   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9439   if (anchorSection) *anchorSection = plex->anchorSection;
9440   if (anchorIS) *anchorIS = plex->anchorIS;
9441   PetscFunctionReturn(0);
9442 }
9443 
9444 /*@
9445   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.  Unlike boundary conditions,
9446   when a point's degrees of freedom in a section are constrained to an outside value, the anchor constraints set a
9447   point's degrees of freedom to be a linear combination of other points' degrees of freedom.
9448 
9449   After specifying the layout of constraints with DMPlexSetAnchors(), one specifies the constraints by calling
9450   DMGetDefaultConstraints() and filling in the entries in the constraint matrix.
9451 
9452   collective on dm
9453 
9454   Input Parameters:
9455 + dm - The DMPlex object
9456 . 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).
9457 - anchorIS - The list of all anchor points.  Must have a local communicator (PETSC_COMM_SELF or derivative).
9458 
9459   The reference counts of anchorSection and anchorIS are incremented.
9460 
9461   Level: intermediate
9462 
9463 .seealso: `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9464 @*/
9465 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
9466 {
9467   DM_Plex        *plex = (DM_Plex *)dm->data;
9468   PetscMPIInt    result;
9469 
9470   PetscFunctionBegin;
9471   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9472   if (anchorSection) {
9473     PetscValidHeaderSpecific(anchorSection,PETSC_SECTION_CLASSID,2);
9474     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF,PetscObjectComm((PetscObject)anchorSection),&result));
9475     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT,PETSC_COMM_SELF,PETSC_ERR_ARG_NOTSAMECOMM,"anchor section must have local communicator");
9476   }
9477   if (anchorIS) {
9478     PetscValidHeaderSpecific(anchorIS,IS_CLASSID,3);
9479     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF,PetscObjectComm((PetscObject)anchorIS),&result));
9480     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT,PETSC_COMM_SELF,PETSC_ERR_ARG_NOTSAMECOMM,"anchor IS must have local communicator");
9481   }
9482 
9483   PetscCall(PetscObjectReference((PetscObject)anchorSection));
9484   PetscCall(PetscSectionDestroy(&plex->anchorSection));
9485   plex->anchorSection = anchorSection;
9486 
9487   PetscCall(PetscObjectReference((PetscObject)anchorIS));
9488   PetscCall(ISDestroy(&plex->anchorIS));
9489   plex->anchorIS = anchorIS;
9490 
9491   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
9492     PetscInt size, a, pStart, pEnd;
9493     const PetscInt *anchors;
9494 
9495     PetscCall(PetscSectionGetChart(anchorSection,&pStart,&pEnd));
9496     PetscCall(ISGetLocalSize(anchorIS,&size));
9497     PetscCall(ISGetIndices(anchorIS,&anchors));
9498     for (a = 0; a < size; a++) {
9499       PetscInt p;
9500 
9501       p = anchors[a];
9502       if (p >= pStart && p < pEnd) {
9503         PetscInt dof;
9504 
9505         PetscCall(PetscSectionGetDof(anchorSection,p,&dof));
9506         if (dof) {
9507 
9508           PetscCall(ISRestoreIndices(anchorIS,&anchors));
9509           SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Point %" PetscInt_FMT " cannot be constrained and an anchor",p);
9510         }
9511       }
9512     }
9513     PetscCall(ISRestoreIndices(anchorIS,&anchors));
9514   }
9515   /* reset the generic constraints */
9516   PetscCall(DMSetDefaultConstraints(dm,NULL,NULL,NULL));
9517   PetscFunctionReturn(0);
9518 }
9519 
9520 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
9521 {
9522   PetscSection anchorSection;
9523   PetscInt pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
9524 
9525   PetscFunctionBegin;
9526   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9527   PetscCall(DMPlexGetAnchors(dm,&anchorSection,NULL));
9528   PetscCall(PetscSectionCreate(PETSC_COMM_SELF,cSec));
9529   PetscCall(PetscSectionGetNumFields(section,&numFields));
9530   if (numFields) {
9531     PetscInt f;
9532     PetscCall(PetscSectionSetNumFields(*cSec,numFields));
9533 
9534     for (f = 0; f < numFields; f++) {
9535       PetscInt numComp;
9536 
9537       PetscCall(PetscSectionGetFieldComponents(section,f,&numComp));
9538       PetscCall(PetscSectionSetFieldComponents(*cSec,f,numComp));
9539     }
9540   }
9541   PetscCall(PetscSectionGetChart(anchorSection,&pStart,&pEnd));
9542   PetscCall(PetscSectionGetChart(section,&sStart,&sEnd));
9543   pStart = PetscMax(pStart,sStart);
9544   pEnd   = PetscMin(pEnd,sEnd);
9545   pEnd   = PetscMax(pStart,pEnd);
9546   PetscCall(PetscSectionSetChart(*cSec,pStart,pEnd));
9547   for (p = pStart; p < pEnd; p++) {
9548     PetscCall(PetscSectionGetDof(anchorSection,p,&dof));
9549     if (dof) {
9550       PetscCall(PetscSectionGetDof(section,p,&dof));
9551       PetscCall(PetscSectionSetDof(*cSec,p,dof));
9552       for (f = 0; f < numFields; f++) {
9553         PetscCall(PetscSectionGetFieldDof(section,p,f,&dof));
9554         PetscCall(PetscSectionSetFieldDof(*cSec,p,f,dof));
9555       }
9556     }
9557   }
9558   PetscCall(PetscSectionSetUp(*cSec));
9559   PetscCall(PetscObjectSetName((PetscObject) *cSec, "Constraint Section"));
9560   PetscFunctionReturn(0);
9561 }
9562 
9563 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
9564 {
9565   PetscSection   aSec;
9566   PetscInt       pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
9567   const PetscInt *anchors;
9568   PetscInt       numFields, f;
9569   IS             aIS;
9570   MatType        mtype;
9571   PetscBool      iscuda,iskokkos;
9572 
9573   PetscFunctionBegin;
9574   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9575   PetscCall(PetscSectionGetStorageSize(cSec, &m));
9576   PetscCall(PetscSectionGetStorageSize(section, &n));
9577   PetscCall(MatCreate(PETSC_COMM_SELF,cMat));
9578   PetscCall(MatSetSizes(*cMat,m,n,m,n));
9579   PetscCall(PetscStrcmp(dm->mattype,MATSEQAIJCUSPARSE,&iscuda));
9580   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype,MATMPIAIJCUSPARSE,&iscuda));
9581   PetscCall(PetscStrcmp(dm->mattype,MATSEQAIJKOKKOS,&iskokkos));
9582   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype,MATMPIAIJKOKKOS,&iskokkos));
9583   if (iscuda) mtype = MATSEQAIJCUSPARSE;
9584   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
9585   else mtype = MATSEQAIJ;
9586   PetscCall(MatSetType(*cMat,mtype));
9587   PetscCall(DMPlexGetAnchors(dm,&aSec,&aIS));
9588   PetscCall(ISGetIndices(aIS,&anchors));
9589   /* cSec will be a subset of aSec and section */
9590   PetscCall(PetscSectionGetChart(cSec,&pStart,&pEnd));
9591   PetscCall(PetscSectionGetChart(section,&sStart,&sEnd));
9592   PetscCall(PetscMalloc1(m+1,&i));
9593   i[0] = 0;
9594   PetscCall(PetscSectionGetNumFields(section,&numFields));
9595   for (p = pStart; p < pEnd; p++) {
9596     PetscInt rDof, rOff, r;
9597 
9598     PetscCall(PetscSectionGetDof(aSec,p,&rDof));
9599     if (!rDof) continue;
9600     PetscCall(PetscSectionGetOffset(aSec,p,&rOff));
9601     if (numFields) {
9602       for (f = 0; f < numFields; f++) {
9603         annz = 0;
9604         for (r = 0; r < rDof; r++) {
9605           a = anchors[rOff + r];
9606           if (a < sStart || a >= sEnd) continue;
9607           PetscCall(PetscSectionGetFieldDof(section,a,f,&aDof));
9608           annz += aDof;
9609         }
9610         PetscCall(PetscSectionGetFieldDof(cSec,p,f,&dof));
9611         PetscCall(PetscSectionGetFieldOffset(cSec,p,f,&off));
9612         for (q = 0; q < dof; q++) {
9613           i[off + q + 1] = i[off + q] + annz;
9614         }
9615       }
9616     } else {
9617       annz = 0;
9618       PetscCall(PetscSectionGetDof(cSec,p,&dof));
9619       for (q = 0; q < dof; q++) {
9620         a = anchors[rOff + q];
9621         if (a < sStart || a >= sEnd) continue;
9622         PetscCall(PetscSectionGetDof(section,a,&aDof));
9623         annz += aDof;
9624       }
9625       PetscCall(PetscSectionGetDof(cSec,p,&dof));
9626       PetscCall(PetscSectionGetOffset(cSec,p,&off));
9627       for (q = 0; q < dof; q++) {
9628         i[off + q + 1] = i[off + q] + annz;
9629       }
9630     }
9631   }
9632   nnz = i[m];
9633   PetscCall(PetscMalloc1(nnz,&j));
9634   offset = 0;
9635   for (p = pStart; p < pEnd; p++) {
9636     if (numFields) {
9637       for (f = 0; f < numFields; f++) {
9638         PetscCall(PetscSectionGetFieldDof(cSec,p,f,&dof));
9639         for (q = 0; q < dof; q++) {
9640           PetscInt rDof, rOff, r;
9641           PetscCall(PetscSectionGetDof(aSec,p,&rDof));
9642           PetscCall(PetscSectionGetOffset(aSec,p,&rOff));
9643           for (r = 0; r < rDof; r++) {
9644             PetscInt s;
9645 
9646             a = anchors[rOff + r];
9647             if (a < sStart || a >= sEnd) continue;
9648             PetscCall(PetscSectionGetFieldDof(section,a,f,&aDof));
9649             PetscCall(PetscSectionGetFieldOffset(section,a,f,&aOff));
9650             for (s = 0; s < aDof; s++) {
9651               j[offset++] = aOff + s;
9652             }
9653           }
9654         }
9655       }
9656     } else {
9657       PetscCall(PetscSectionGetDof(cSec,p,&dof));
9658       for (q = 0; q < dof; q++) {
9659         PetscInt rDof, rOff, r;
9660         PetscCall(PetscSectionGetDof(aSec,p,&rDof));
9661         PetscCall(PetscSectionGetOffset(aSec,p,&rOff));
9662         for (r = 0; r < rDof; r++) {
9663           PetscInt s;
9664 
9665           a = anchors[rOff + r];
9666           if (a < sStart || a >= sEnd) continue;
9667           PetscCall(PetscSectionGetDof(section,a,&aDof));
9668           PetscCall(PetscSectionGetOffset(section,a,&aOff));
9669           for (s = 0; s < aDof; s++) {
9670             j[offset++] = aOff + s;
9671           }
9672         }
9673       }
9674     }
9675   }
9676   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat,i,j,NULL));
9677   PetscCall(PetscFree(i));
9678   PetscCall(PetscFree(j));
9679   PetscCall(ISRestoreIndices(aIS,&anchors));
9680   PetscFunctionReturn(0);
9681 }
9682 
9683 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
9684 {
9685   DM_Plex        *plex = (DM_Plex *)dm->data;
9686   PetscSection   anchorSection, section, cSec;
9687   Mat            cMat;
9688 
9689   PetscFunctionBegin;
9690   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9691   PetscCall(DMPlexGetAnchors(dm,&anchorSection,NULL));
9692   if (anchorSection) {
9693     PetscInt Nf;
9694 
9695     PetscCall(DMGetLocalSection(dm,&section));
9696     PetscCall(DMPlexCreateConstraintSection_Anchors(dm,section,&cSec));
9697     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm,section,cSec,&cMat));
9698     PetscCall(DMGetNumFields(dm,&Nf));
9699     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm,section,cSec,cMat));
9700     PetscCall(DMSetDefaultConstraints(dm,cSec,cMat,NULL));
9701     PetscCall(PetscSectionDestroy(&cSec));
9702     PetscCall(MatDestroy(&cMat));
9703   }
9704   PetscFunctionReturn(0);
9705 }
9706 
9707 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
9708 {
9709   IS             subis;
9710   PetscSection   section, subsection;
9711 
9712   PetscFunctionBegin;
9713   PetscCall(DMGetLocalSection(dm, &section));
9714   PetscCheck(section,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
9715   PetscCheck(subdm,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
9716   /* Create subdomain */
9717   PetscCall(DMPlexFilter(dm, label, value, subdm));
9718   /* Create submodel */
9719   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
9720   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
9721   PetscCall(DMSetLocalSection(*subdm, subsection));
9722   PetscCall(PetscSectionDestroy(&subsection));
9723   PetscCall(DMCopyDisc(dm, *subdm));
9724   /* Create map from submodel to global model */
9725   if (is) {
9726     PetscSection    sectionGlobal, subsectionGlobal;
9727     IS              spIS;
9728     const PetscInt *spmap;
9729     PetscInt       *subIndices;
9730     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
9731     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
9732 
9733     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
9734     PetscCall(ISGetIndices(spIS, &spmap));
9735     PetscCall(PetscSectionGetNumFields(section, &Nf));
9736     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
9737     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
9738     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
9739     for (p = pStart; p < pEnd; ++p) {
9740       PetscInt gdof, pSubSize  = 0;
9741 
9742       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
9743       if (gdof > 0) {
9744         for (f = 0; f < Nf; ++f) {
9745           PetscInt fdof, fcdof;
9746 
9747           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
9748           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
9749           pSubSize += fdof-fcdof;
9750         }
9751         subSize += pSubSize;
9752         if (pSubSize) {
9753           if (bs < 0) {
9754             bs = pSubSize;
9755           } else if (bs != pSubSize) {
9756             /* Layout does not admit a pointwise block size */
9757             bs = 1;
9758           }
9759         }
9760       }
9761     }
9762     /* Must have same blocksize on all procs (some might have no points) */
9763     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs; bsLocal[1] = bs;
9764     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject) dm), bsLocal, bsMinMax));
9765     if (bsMinMax[0] != bsMinMax[1]) {bs = 1;}
9766     else                            {bs = bsMinMax[0];}
9767     PetscCall(PetscMalloc1(subSize, &subIndices));
9768     for (p = pStart; p < pEnd; ++p) {
9769       PetscInt gdof, goff;
9770 
9771       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
9772       if (gdof > 0) {
9773         const PetscInt point = spmap[p];
9774 
9775         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
9776         for (f = 0; f < Nf; ++f) {
9777           PetscInt fdof, fcdof, fc, f2, poff = 0;
9778 
9779           /* Can get rid of this loop by storing field information in the global section */
9780           for (f2 = 0; f2 < f; ++f2) {
9781             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
9782             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
9783             poff += fdof-fcdof;
9784           }
9785           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
9786           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
9787           for (fc = 0; fc < fdof-fcdof; ++fc, ++subOff) {
9788             subIndices[subOff] = goff+poff+fc;
9789           }
9790         }
9791       }
9792     }
9793     PetscCall(ISRestoreIndices(spIS, &spmap));
9794     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
9795     if (bs > 1) {
9796       /* We need to check that the block size does not come from non-contiguous fields */
9797       PetscInt i, j, set = 1;
9798       for (i = 0; i < subSize; i += bs) {
9799         for (j = 0; j < bs; ++j) {
9800           if (subIndices[i+j] != subIndices[i]+j) {set = 0; break;}
9801         }
9802       }
9803       if (set) PetscCall(ISSetBlockSize(*is, bs));
9804     }
9805     /* Attach nullspace */
9806     for (f = 0; f < Nf; ++f) {
9807       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
9808       if ((*subdm)->nullspaceConstructors[f]) break;
9809     }
9810     if (f < Nf) {
9811       MatNullSpace nullSpace;
9812       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
9813 
9814       PetscCall(PetscObjectCompose((PetscObject) *is, "nullspace", (PetscObject) nullSpace));
9815       PetscCall(MatNullSpaceDestroy(&nullSpace));
9816     }
9817   }
9818   PetscFunctionReturn(0);
9819 }
9820 
9821 /*@
9822   DMPlexMonitorThroughput - Report the cell throughput of FE integration
9823 
9824   Input Parameter:
9825 - dm - The DM
9826 
9827   Level: developer
9828 
9829   Options Database Keys:
9830 . -dm_plex_monitor_throughput - Activate the monitor
9831 
9832 .seealso: `DMSetFromOptions()`, `DMPlexCreate()`
9833 @*/
9834 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
9835 {
9836 #if defined(PETSC_USE_LOG)
9837   PetscStageLog      stageLog;
9838   PetscLogEvent      event;
9839   PetscLogStage      stage;
9840   PetscEventPerfInfo eventInfo;
9841   PetscReal          cellRate, flopRate;
9842   PetscInt           cStart, cEnd, Nf, N;
9843   const char        *name;
9844 #endif
9845 
9846   PetscFunctionBegin;
9847   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9848 #if defined(PETSC_USE_LOG)
9849   PetscCall(PetscObjectGetName((PetscObject) dm, &name));
9850   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9851   PetscCall(DMGetNumFields(dm, &Nf));
9852   PetscCall(PetscLogGetStageLog(&stageLog));
9853   PetscCall(PetscStageLogGetCurrent(stageLog, &stage));
9854   PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
9855   PetscCall(PetscLogEventGetPerfInfo(stage, event, &eventInfo));
9856   N        = (cEnd - cStart)*Nf*eventInfo.count;
9857   flopRate = eventInfo.flops/eventInfo.time;
9858   cellRate = N/eventInfo.time;
9859   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)));
9860 #else
9861   SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Plex Throughput Monitor is not supported if logging is turned off. Reconfigure using --with-log.");
9862 #endif
9863   PetscFunctionReturn(0);
9864 }
9865