xref: /petsc/src/dm/impls/plex/plex.c (revision 8fb5bd83c3955fefcf33a54e3bb66920a9fa884b)
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 
14 PETSC_EXTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
15 
16 /*@
17   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
18 
19   Input Parameter:
20 . dm      - The DMPlex object
21 
22   Output Parameter:
23 . simplex - Flag checking for a simplex
24 
25   Note: This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
26   If the mesh has no cells, this returns PETSC_FALSE.
27 
28   Level: intermediate
29 
30 .seealso `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
31 @*/
32 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
33 {
34   DMPolytopeType ct;
35   PetscInt       cStart, cEnd;
36 
37   PetscFunctionBegin;
38   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
39   if (cEnd <= cStart) {*simplex = PETSC_FALSE; PetscFunctionReturn(0);}
40   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
41   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct)+1 ? PETSC_TRUE : PETSC_FALSE;
42   PetscFunctionReturn(0);
43 }
44 
45 /*@
46   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
47 
48   Input Parameters:
49 + dm     - The DMPlex object
50 - height - The cell height in the Plex, 0 is the default
51 
52   Output Parameters:
53 + cStart - The first "normal" cell
54 - cEnd   - The upper bound on "normal"" cells
55 
56   Note: This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
57 
58   Level: developer
59 
60 .seealso `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
61 @*/
62 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd)
63 {
64   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
65   PetscInt       cS, cE, c;
66 
67   PetscFunctionBegin;
68   PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), &cS, &cE));
69   for (c = cS; c < cE; ++c) {
70     DMPolytopeType cct;
71 
72     PetscCall(DMPlexGetCellType(dm, c, &cct));
73     if ((PetscInt) cct < 0) break;
74     switch (cct) {
75       case DM_POLYTOPE_POINT:
76       case DM_POLYTOPE_SEGMENT:
77       case DM_POLYTOPE_TRIANGLE:
78       case DM_POLYTOPE_QUADRILATERAL:
79       case DM_POLYTOPE_TETRAHEDRON:
80       case DM_POLYTOPE_HEXAHEDRON:
81         ct = cct;
82         break;
83       default: break;
84     }
85     if (ct != DM_POLYTOPE_UNKNOWN) break;
86   }
87   if (ct != DM_POLYTOPE_UNKNOWN) {
88     DMLabel ctLabel;
89 
90     PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
91     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &cS, &cE));
92   }
93   if (cStart) *cStart = cS;
94   if (cEnd)   *cEnd   = cE;
95   PetscFunctionReturn(0);
96 }
97 
98 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
99 {
100   PetscInt       cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
101   PetscInt       vcdof[2] = {0,0}, globalvcdof[2];
102 
103   PetscFunctionBegin;
104   *ft  = PETSC_VTK_INVALID;
105   PetscCall(DMGetCoordinateDim(dm, &cdim));
106   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
107   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
108   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
109   if (field >= 0) {
110     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
111     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
112   } else {
113     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
114     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
115   }
116   PetscCallMPI(MPI_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
117   if (globalvcdof[0]) {
118     *sStart = vStart;
119     *sEnd   = vEnd;
120     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
121     else                        *ft = PETSC_VTK_POINT_FIELD;
122   } else if (globalvcdof[1]) {
123     *sStart = cStart;
124     *sEnd   = cEnd;
125     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
126     else                        *ft = PETSC_VTK_CELL_FIELD;
127   } else {
128     if (field >= 0) {
129       const char *fieldname;
130 
131       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
132       PetscCall(PetscInfo((PetscObject) dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
133     } else {
134       PetscCall(PetscInfo((PetscObject) dm, "Could not classify VTK output type of section\n"));
135     }
136   }
137   PetscFunctionReturn(0);
138 }
139 
140 /*@
141   DMPlexVecView1D - Plot many 1D solutions on the same line graph
142 
143   Collective on dm
144 
145   Input Parameters:
146 + dm - The DMPlex
147 . n  - The number of vectors
148 . u  - The array of local vectors
149 - viewer - The Draw viewer
150 
151   Level: advanced
152 
153 .seealso: `VecViewFromOptions()`, `VecView()`
154 @*/
155 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
156 {
157   PetscDS            ds;
158   PetscDraw          draw = NULL;
159   PetscDrawLG        lg;
160   Vec                coordinates;
161   const PetscScalar *coords, **sol;
162   PetscReal         *vals;
163   PetscInt          *Nc;
164   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
165   char             **names;
166 
167   PetscFunctionBegin;
168   PetscCall(DMGetDS(dm, &ds));
169   PetscCall(PetscDSGetNumFields(ds, &Nf));
170   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
171   PetscCall(PetscDSGetComponents(ds, &Nc));
172 
173   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
174   if (!draw) PetscFunctionReturn(0);
175   PetscCall(PetscDrawLGCreate(draw, n*Nl, &lg));
176 
177   PetscCall(PetscMalloc3(n, &sol, n*Nl, &names, n*Nl, &vals));
178   for (i = 0, l = 0; i < n; ++i) {
179     const char *vname;
180 
181     PetscCall(PetscObjectGetName((PetscObject) u[i], &vname));
182     for (f = 0; f < Nf; ++f) {
183       PetscObject disc;
184       const char *fname;
185       char        tmpname[PETSC_MAX_PATH_LEN];
186 
187       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
188       /* TODO Create names for components */
189       for (c = 0; c < Nc[f]; ++c, ++l) {
190         PetscCall(PetscObjectGetName(disc, &fname));
191         PetscCall(PetscStrcpy(tmpname, vname));
192         PetscCall(PetscStrlcat(tmpname, ":", PETSC_MAX_PATH_LEN));
193         PetscCall(PetscStrlcat(tmpname, fname, PETSC_MAX_PATH_LEN));
194         PetscCall(PetscStrallocpy(tmpname, &names[l]));
195       }
196     }
197   }
198   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *) names));
199   /* Just add P_1 support for now */
200   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
201   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
202   PetscCall(VecGetArrayRead(coordinates, &coords));
203   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
204   for (v = vStart; v < vEnd; ++v) {
205     PetscScalar *x, *svals;
206 
207     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
208     for (i = 0; i < n; ++i) {
209       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
210       for (l = 0; l < Nl; ++l) vals[i*Nl + l] = PetscRealPart(svals[l]);
211     }
212     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
213   }
214   PetscCall(VecRestoreArrayRead(coordinates, &coords));
215   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
216   for (l = 0; l < n*Nl; ++l) PetscCall(PetscFree(names[l]));
217   PetscCall(PetscFree3(sol, names, vals));
218 
219   PetscCall(PetscDrawLGDraw(lg));
220   PetscCall(PetscDrawLGDestroy(&lg));
221   PetscFunctionReturn(0);
222 }
223 
224 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
225 {
226   DM             dm;
227 
228   PetscFunctionBegin;
229   PetscCall(VecGetDM(u, &dm));
230   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
231   PetscFunctionReturn(0);
232 }
233 
234 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
235 {
236   DM                 dm;
237   PetscSection       s;
238   PetscDraw          draw, popup;
239   DM                 cdm;
240   PetscSection       coordSection;
241   Vec                coordinates;
242   const PetscScalar *coords, *array;
243   PetscReal          bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
244   PetscReal          vbound[2], time;
245   PetscBool          flg;
246   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
247   const char        *name;
248   char               title[PETSC_MAX_PATH_LEN];
249 
250   PetscFunctionBegin;
251   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
252   PetscCall(VecGetDM(v, &dm));
253   PetscCall(DMGetCoordinateDim(dm, &dim));
254   PetscCall(DMGetLocalSection(dm, &s));
255   PetscCall(PetscSectionGetNumFields(s, &Nf));
256   PetscCall(DMGetCoarsenLevel(dm, &level));
257   PetscCall(DMGetCoordinateDM(dm, &cdm));
258   PetscCall(DMGetLocalSection(cdm, &coordSection));
259   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
260   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
261   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
262 
263   PetscCall(PetscObjectGetName((PetscObject) v, &name));
264   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
265 
266   PetscCall(VecGetLocalSize(coordinates, &N));
267   PetscCall(VecGetArrayRead(coordinates, &coords));
268   for (c = 0; c < N; c += dim) {
269     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));   bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
270     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c+1])); bound[3] = PetscMax(bound[3], PetscRealPart(coords[c+1]));
271   }
272   PetscCall(VecRestoreArrayRead(coordinates, &coords));
273   PetscCall(PetscDrawClear(draw));
274 
275   /* Could implement something like DMDASelectFields() */
276   for (f = 0; f < Nf; ++f) {
277     DM   fdm = dm;
278     Vec  fv  = v;
279     IS   fis;
280     char prefix[PETSC_MAX_PATH_LEN];
281     const char *fname;
282 
283     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
284     PetscCall(PetscSectionGetFieldName(s, f, &fname));
285 
286     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix,sizeof(prefix)));
287     else               {prefix[0] = '\0';}
288     if (Nf > 1) {
289       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
290       PetscCall(VecGetSubVector(v, fis, &fv));
291       PetscCall(PetscStrlcat(prefix, fname,sizeof(prefix)));
292       PetscCall(PetscStrlcat(prefix, "_",sizeof(prefix)));
293     }
294     for (comp = 0; comp < Nc; ++comp, ++w) {
295       PetscInt nmax = 2;
296 
297       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
298       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
299       else        PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
300       PetscCall(PetscDrawSetTitle(draw, title));
301 
302       /* TODO Get max and min only for this component */
303       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
304       if (!flg) {
305         PetscCall(VecMin(fv, NULL, &vbound[0]));
306         PetscCall(VecMax(fv, NULL, &vbound[1]));
307         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
308       }
309       PetscCall(PetscDrawGetPopup(draw, &popup));
310       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
311       PetscCall(PetscDrawSetCoordinates(draw, bound[0], bound[1], bound[2], bound[3]));
312 
313       PetscCall(VecGetArrayRead(fv, &array));
314       for (c = cStart; c < cEnd; ++c) {
315         PetscScalar *coords = NULL, *a = NULL;
316         PetscInt     numCoords, color[4] = {-1,-1,-1,-1};
317 
318         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
319         if (a) {
320           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
321           color[1] = color[2] = color[3] = color[0];
322         } else {
323           PetscScalar *vals = NULL;
324           PetscInt     numVals, va;
325 
326           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
327           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);
328           switch (numVals/Nc) {
329           case 3: /* P1 Triangle */
330           case 4: /* P1 Quadrangle */
331             for (va = 0; va < numVals/Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va*Nc+comp]), vbound[0], vbound[1]);
332             break;
333           case 6: /* P2 Triangle */
334           case 8: /* P2 Quadrangle */
335             for (va = 0; va < numVals/(Nc*2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va*Nc+comp + numVals/(Nc*2)]), vbound[0], vbound[1]);
336             break;
337           default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals/Nc);
338           }
339           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
340         }
341         PetscCall(DMPlexVecGetClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
342         switch (numCoords) {
343         case 6:
344         case 12: /* Localized triangle */
345           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]));
346           break;
347         case 8:
348         case 16: /* Localized quadrilateral */
349           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]));
350           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]));
351           break;
352         default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
353         }
354         PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
355       }
356       PetscCall(VecRestoreArrayRead(fv, &array));
357       PetscCall(PetscDrawFlush(draw));
358       PetscCall(PetscDrawPause(draw));
359       PetscCall(PetscDrawSave(draw));
360     }
361     if (Nf > 1) {
362       PetscCall(VecRestoreSubVector(v, fis, &fv));
363       PetscCall(ISDestroy(&fis));
364       PetscCall(DMDestroy(&fdm));
365     }
366   }
367   PetscFunctionReturn(0);
368 }
369 
370 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
371 {
372   DM        dm;
373   PetscDraw draw;
374   PetscInt  dim;
375   PetscBool isnull;
376 
377   PetscFunctionBegin;
378   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
379   PetscCall(PetscDrawIsNull(draw, &isnull));
380   if (isnull) PetscFunctionReturn(0);
381 
382   PetscCall(VecGetDM(v, &dm));
383   PetscCall(DMGetCoordinateDim(dm, &dim));
384   switch (dim) {
385   case 1: PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));break;
386   case 2: PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));break;
387   default: SETERRQ(PetscObjectComm((PetscObject) v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
388   }
389   PetscFunctionReturn(0);
390 }
391 
392 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
393 {
394   DM                      dm;
395   Vec                     locv;
396   const char              *name;
397   PetscSection            section;
398   PetscInt                pStart, pEnd;
399   PetscInt                numFields;
400   PetscViewerVTKFieldType ft;
401 
402   PetscFunctionBegin;
403   PetscCall(VecGetDM(v, &dm));
404   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
405   PetscCall(PetscObjectGetName((PetscObject) v, &name));
406   PetscCall(PetscObjectSetName((PetscObject) locv, name));
407   PetscCall(VecCopy(v, locv));
408   PetscCall(DMGetLocalSection(dm, &section));
409   PetscCall(PetscSectionGetNumFields(section, &numFields));
410   if (!numFields) {
411     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
412     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject) dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE,(PetscObject) locv));
413   } else {
414     PetscInt f;
415 
416     for (f = 0; f < numFields; f++) {
417       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
418       if (ft == PETSC_VTK_INVALID) continue;
419       PetscCall(PetscObjectReference((PetscObject)locv));
420       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject) dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE,(PetscObject) locv));
421     }
422     PetscCall(VecDestroy(&locv));
423   }
424   PetscFunctionReturn(0);
425 }
426 
427 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
428 {
429   DM             dm;
430   PetscBool      isvtk, ishdf5, isdraw, isglvis;
431 
432   PetscFunctionBegin;
433   PetscCall(VecGetDM(v, &dm));
434   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
435   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,   &isvtk));
436   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,  &ishdf5));
437   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERDRAW,  &isdraw));
438   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERGLVIS, &isglvis));
439   if (isvtk || ishdf5 || isdraw || isglvis) {
440     PetscInt    i,numFields;
441     PetscObject fe;
442     PetscBool   fem = PETSC_FALSE;
443     Vec         locv = v;
444     const char  *name;
445     PetscInt    step;
446     PetscReal   time;
447 
448     PetscCall(DMGetNumFields(dm, &numFields));
449     for (i=0; i<numFields; i++) {
450       PetscCall(DMGetField(dm, i, NULL, &fe));
451       if (fe->classid == PETSCFE_CLASSID) { fem = PETSC_TRUE; break; }
452     }
453     if (fem) {
454       PetscObject isZero;
455 
456       PetscCall(DMGetLocalVector(dm, &locv));
457       PetscCall(PetscObjectGetName((PetscObject) v, &name));
458       PetscCall(PetscObjectSetName((PetscObject) locv, name));
459       PetscCall(PetscObjectQuery((PetscObject) v, "__Vec_bc_zero__", &isZero));
460       PetscCall(PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", isZero));
461       PetscCall(VecCopy(v, locv));
462       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
463       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
464     }
465     if (isvtk) {
466       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
467     } else if (ishdf5) {
468 #if defined(PETSC_HAVE_HDF5)
469       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
470 #else
471       SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
472 #endif
473     } else if (isdraw) {
474       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
475     } else if (isglvis) {
476       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
477       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
478       PetscCall(VecView_GLVis(locv, viewer));
479     }
480     if (fem) {
481       PetscCall(PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", NULL));
482       PetscCall(DMRestoreLocalVector(dm, &locv));
483     }
484   } else {
485     PetscBool isseq;
486 
487     PetscCall(PetscObjectTypeCompare((PetscObject) v, VECSEQ, &isseq));
488     if (isseq) PetscCall(VecView_Seq(v, viewer));
489     else       PetscCall(VecView_MPI(v, viewer));
490   }
491   PetscFunctionReturn(0);
492 }
493 
494 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
495 {
496   DM        dm;
497   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii;
498 
499   PetscFunctionBegin;
500   PetscCall(VecGetDM(v, &dm));
501   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
502   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,      &isvtk));
503   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,     &ishdf5));
504   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERDRAW,     &isdraw));
505   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERGLVIS,    &isglvis));
506   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWEREXODUSII, &isexodusii));
507   if (isvtk || isdraw || isglvis) {
508     Vec         locv;
509     PetscObject isZero;
510     const char *name;
511 
512     PetscCall(DMGetLocalVector(dm, &locv));
513     PetscCall(PetscObjectGetName((PetscObject) v, &name));
514     PetscCall(PetscObjectSetName((PetscObject) locv, name));
515     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
516     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
517     PetscCall(PetscObjectQuery((PetscObject) v, "__Vec_bc_zero__", &isZero));
518     PetscCall(PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", isZero));
519     PetscCall(VecView_Plex_Local(locv, viewer));
520     PetscCall(PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", NULL));
521     PetscCall(DMRestoreLocalVector(dm, &locv));
522   } else if (ishdf5) {
523 #if defined(PETSC_HAVE_HDF5)
524     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
525 #else
526     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
527 #endif
528   } else if (isexodusii) {
529 #if defined(PETSC_HAVE_EXODUSII)
530     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
531 #else
532     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
533 #endif
534   } else {
535     PetscBool isseq;
536 
537     PetscCall(PetscObjectTypeCompare((PetscObject) v, VECSEQ, &isseq));
538     if (isseq) PetscCall(VecView_Seq(v, viewer));
539     else       PetscCall(VecView_MPI(v, viewer));
540   }
541   PetscFunctionReturn(0);
542 }
543 
544 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
545 {
546   DM                dm;
547   MPI_Comm          comm;
548   PetscViewerFormat format;
549   Vec               v;
550   PetscBool         isvtk, ishdf5;
551 
552   PetscFunctionBegin;
553   PetscCall(VecGetDM(originalv, &dm));
554   PetscCall(PetscObjectGetComm((PetscObject) originalv, &comm));
555   PetscCheck(dm,comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
556   PetscCall(PetscViewerGetFormat(viewer, &format));
557   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
558   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,  &isvtk));
559   if (format == PETSC_VIEWER_NATIVE) {
560     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
561     /* this need a better fix */
562     if (dm->useNatural) {
563       if (dm->sfNatural) {
564         const char *vecname;
565         PetscInt    n, nroots;
566 
567         PetscCall(VecGetLocalSize(originalv, &n));
568         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
569         if (n == nroots) {
570           PetscCall(DMGetGlobalVector(dm, &v));
571           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
572           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
573           PetscCall(PetscObjectGetName((PetscObject) originalv, &vecname));
574           PetscCall(PetscObjectSetName((PetscObject) v, vecname));
575         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
576       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
577     } else v = originalv;
578   } else v = originalv;
579 
580   if (ishdf5) {
581 #if defined(PETSC_HAVE_HDF5)
582     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
583 #else
584     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
585 #endif
586   } else if (isvtk) {
587     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
588   } else {
589     PetscBool isseq;
590 
591     PetscCall(PetscObjectTypeCompare((PetscObject) v, VECSEQ, &isseq));
592     if (isseq) PetscCall(VecView_Seq(v, viewer));
593     else       PetscCall(VecView_MPI(v, viewer));
594   }
595   if (v != originalv) PetscCall(DMRestoreGlobalVector(dm, &v));
596   PetscFunctionReturn(0);
597 }
598 
599 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
600 {
601   DM             dm;
602   PetscBool      ishdf5;
603 
604   PetscFunctionBegin;
605   PetscCall(VecGetDM(v, &dm));
606   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
607   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
608   if (ishdf5) {
609     DM          dmBC;
610     Vec         gv;
611     const char *name;
612 
613     PetscCall(DMGetOutputDM(dm, &dmBC));
614     PetscCall(DMGetGlobalVector(dmBC, &gv));
615     PetscCall(PetscObjectGetName((PetscObject) v, &name));
616     PetscCall(PetscObjectSetName((PetscObject) gv, name));
617     PetscCall(VecLoad_Default(gv, viewer));
618     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
619     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
620     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
621   } else PetscCall(VecLoad_Default(v, viewer));
622   PetscFunctionReturn(0);
623 }
624 
625 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
626 {
627   DM             dm;
628   PetscBool      ishdf5,isexodusii;
629 
630   PetscFunctionBegin;
631   PetscCall(VecGetDM(v, &dm));
632   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
633   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,     &ishdf5));
634   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWEREXODUSII, &isexodusii));
635   if (ishdf5) {
636 #if defined(PETSC_HAVE_HDF5)
637     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
638 #else
639     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
640 #endif
641   } else if (isexodusii) {
642 #if defined(PETSC_HAVE_EXODUSII)
643     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
644 #else
645     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
646 #endif
647   } else PetscCall(VecLoad_Default(v, viewer));
648   PetscFunctionReturn(0);
649 }
650 
651 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
652 {
653   DM                dm;
654   PetscViewerFormat format;
655   PetscBool         ishdf5;
656 
657   PetscFunctionBegin;
658   PetscCall(VecGetDM(originalv, &dm));
659   PetscCheck(dm,PetscObjectComm((PetscObject) originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
660   PetscCall(PetscViewerGetFormat(viewer, &format));
661   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
662   if (format == PETSC_VIEWER_NATIVE) {
663     if (dm->useNatural) {
664       if (dm->sfNatural) {
665         if (ishdf5) {
666 #if defined(PETSC_HAVE_HDF5)
667           Vec         v;
668           const char *vecname;
669 
670           PetscCall(DMGetGlobalVector(dm, &v));
671           PetscCall(PetscObjectGetName((PetscObject) originalv, &vecname));
672           PetscCall(PetscObjectSetName((PetscObject) v, vecname));
673           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
674           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
675           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
676           PetscCall(DMRestoreGlobalVector(dm, &v));
677 #else
678           SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
679 #endif
680         } else SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
681       }
682     } else PetscCall(VecLoad_Default(originalv, viewer));
683   }
684   PetscFunctionReturn(0);
685 }
686 
687 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
688 {
689   PetscSection       coordSection;
690   Vec                coordinates;
691   DMLabel            depthLabel, celltypeLabel;
692   const char        *name[4];
693   const PetscScalar *a;
694   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
695 
696   PetscFunctionBegin;
697   PetscCall(DMGetDimension(dm, &dim));
698   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
699   PetscCall(DMGetCoordinateSection(dm, &coordSection));
700   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
701   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
702   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
703   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
704   PetscCall(VecGetArrayRead(coordinates, &a));
705   name[0]     = "vertex";
706   name[1]     = "edge";
707   name[dim-1] = "face";
708   name[dim]   = "cell";
709   for (c = cStart; c < cEnd; ++c) {
710     PetscInt *closure = NULL;
711     PetscInt  closureSize, cl, ct;
712 
713     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
714     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
715     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
716     PetscCall(PetscViewerASCIIPushTab(viewer));
717     for (cl = 0; cl < closureSize*2; cl += 2) {
718       PetscInt point = closure[cl], depth, dof, off, d, p;
719 
720       if ((point < pStart) || (point >= pEnd)) continue;
721       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
722       if (!dof) continue;
723       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
724       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
725       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
726       for (p = 0; p < dof/dim; ++p) {
727         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
728         for (d = 0; d < dim; ++d) {
729           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
730           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double) PetscRealPart(a[off+p*dim+d])));
731         }
732         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
733       }
734       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
735     }
736     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
737     PetscCall(PetscViewerASCIIPopTab(viewer));
738   }
739   PetscCall(VecRestoreArrayRead(coordinates, &a));
740   PetscFunctionReturn(0);
741 }
742 
743 typedef enum {CS_CARTESIAN, CS_POLAR, CS_CYLINDRICAL, CS_SPHERICAL} CoordSystem;
744 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
745 
746 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
747 {
748   PetscInt       i;
749 
750   PetscFunctionBegin;
751   if (dim > 3) {
752     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double) PetscRealPart(x[i])));
753   } else {
754     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
755 
756     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
757     switch (cs) {
758       case CS_CARTESIAN: for (i = 0; i < dim; ++i) trcoords[i] = coords[i];break;
759       case CS_POLAR:
760         PetscCheck(dim == 2,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
761         trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
762         trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
763         break;
764       case CS_CYLINDRICAL:
765         PetscCheck(dim == 3,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
766         trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
767         trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
768         trcoords[2] = coords[2];
769         break;
770       case CS_SPHERICAL:
771         PetscCheck(dim == 3,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
772         trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
773         trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
774         trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
775         break;
776     }
777     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double) trcoords[i]));
778   }
779   PetscFunctionReturn(0);
780 }
781 
782 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
783 {
784   DM_Plex          *mesh = (DM_Plex*) dm->data;
785   DM                cdm, cdmCell;
786   PetscSection      coordSection, coordSectionCell;
787   Vec               coordinates, coordinatesCell;
788   PetscViewerFormat format;
789 
790   PetscFunctionBegin;
791   PetscCall(DMGetCoordinateDM(dm, &cdm));
792   PetscCall(DMGetCoordinateSection(dm, &coordSection));
793   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
794   PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
795   PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
796   PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
797   PetscCall(PetscViewerGetFormat(viewer, &format));
798   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
799     const char *name;
800     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
801     PetscInt    pStart, pEnd, p, numLabels, l;
802     PetscMPIInt rank, size;
803 
804     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
805     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
806     PetscCall(PetscObjectGetName((PetscObject) dm, &name));
807     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
808     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
809     PetscCall(DMGetDimension(dm, &dim));
810     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
811     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
812     else      PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
813     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
814     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
815     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
816     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
817     for (p = pStart; p < pEnd; ++p) {
818       PetscInt dof, off, s;
819 
820       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
821       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
822       for (s = off; s < off+dof; ++s) {
823         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
824       }
825     }
826     PetscCall(PetscViewerFlush(viewer));
827     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
828     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
829     for (p = pStart; p < pEnd; ++p) {
830       PetscInt dof, off, c;
831 
832       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
833       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
834       for (c = off; c < off+dof; ++c) {
835         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " <---- %" PetscInt_FMT " (%" PetscInt_FMT ")\n", rank, p, mesh->cones[c], mesh->coneOrientations[c]));
836       }
837     }
838     PetscCall(PetscViewerFlush(viewer));
839     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
840     if (coordSection && coordinates) {
841       CoordSystem        cs = CS_CARTESIAN;
842       const PetscScalar *array, *arrayCell = NULL;
843       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
844       PetscMPIInt        rank;
845       const char        *name;
846 
847       PetscCall(PetscOptionsGetEnum(((PetscObject) viewer)->options, ((PetscObject) viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *) &cs, NULL));
848       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
849       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
850       PetscCheck(Nf == 1,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
851       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
852       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
853       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
854       pStart =  PetscMin(pvStart, pcStart);
855       pEnd   =  PetscMax(pvEnd,   pcEnd);
856       PetscCall(PetscObjectGetName((PetscObject) coordinates, &name));
857       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
858       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
859       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
860 
861       PetscCall(VecGetArrayRead(coordinates, &array));
862       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
863       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
864       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
865       for (p = pStart; p < pEnd; ++p) {
866         PetscInt dof, off;
867 
868         if (p >= pvStart && p < pvEnd) {
869           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
870           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
871           if (dof) {
872             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
873             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
874             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
875           }
876         }
877         if (cdmCell && p >= pcStart && p < pcEnd) {
878           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
879           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
880           if (dof) {
881             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
882             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
883             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
884           }
885         }
886       }
887       PetscCall(PetscViewerFlush(viewer));
888       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
889       PetscCall(VecRestoreArrayRead(coordinates, &array));
890       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
891     }
892     PetscCall(DMGetNumLabels(dm, &numLabels));
893     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
894     for (l = 0; l < numLabels; ++l) {
895       DMLabel     label;
896       PetscBool   isdepth;
897       const char *name;
898 
899       PetscCall(DMGetLabelName(dm, l, &name));
900       PetscCall(PetscStrcmp(name, "depth", &isdepth));
901       if (isdepth) continue;
902       PetscCall(DMGetLabel(dm, name, &label));
903       PetscCall(DMLabelView(label, viewer));
904     }
905     if (size > 1) {
906       PetscSF sf;
907 
908       PetscCall(DMGetPointSF(dm, &sf));
909       PetscCall(PetscSFView(sf, viewer));
910     }
911     PetscCall(PetscViewerFlush(viewer));
912   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
913     const char  *name, *color;
914     const char  *defcolors[3]  = {"gray", "orange", "green"};
915     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
916     char         lname[PETSC_MAX_PATH_LEN];
917     PetscReal    scale         = 2.0;
918     PetscReal    tikzscale     = 1.0;
919     PetscBool    useNumbers    = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
920     double       tcoords[3];
921     PetscScalar *coords;
922     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
923     PetscMPIInt  rank, size;
924     char         **names, **colors, **lcolors;
925     PetscBool    flg, lflg;
926     PetscBT      wp = NULL;
927     PetscInt     pEnd, pStart;
928 
929     PetscCall(DMGetDimension(dm, &dim));
930     PetscCall(DMPlexGetDepth(dm, &depth));
931     PetscCall(DMGetNumLabels(dm, &numLabels));
932     numLabels  = PetscMax(numLabels, 10);
933     numColors  = 10;
934     numLColors = 10;
935     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
936     PetscCall(PetscOptionsGetReal(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
937     PetscCall(PetscOptionsGetReal(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
938     PetscCall(PetscOptionsGetBool(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
939     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
940     for (d = 0; d < 4; ++d) drawColors[d]  = PETSC_TRUE;
941     n = 4;
942     PetscCall(PetscOptionsGetBoolArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
943     PetscCheck(!flg || n == dim+1,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim+1);
944     PetscCall(PetscOptionsGetBoolArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
945     PetscCheck(!flg || n == dim+1,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim+1);
946     PetscCall(PetscOptionsGetStringArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
947     if (!useLabels) numLabels = 0;
948     PetscCall(PetscOptionsGetStringArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
949     if (!useColors) {
950       numColors = 3;
951       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
952     }
953     PetscCall(PetscOptionsGetStringArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
954     if (!useColors) {
955       numLColors = 4;
956       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
957     }
958     PetscCall(PetscOptionsGetString(((PetscObject) viewer)->options, ((PetscObject) viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
959     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
960     PetscCall(PetscOptionsGetBool(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
961     PetscCheck(!flg || !plotEdges || depth >= dim,PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Mesh must be interpolated");
962     if (depth < dim) plotEdges = PETSC_FALSE;
963     PetscCall(PetscOptionsGetBool(((PetscObject) viewer)->options, ((PetscObject) viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
964 
965     /* filter points with labelvalue != labeldefaultvalue */
966     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
967     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
968     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
969     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
970     if (lflg) {
971       DMLabel lbl;
972 
973       PetscCall(DMGetLabel(dm, lname, &lbl));
974       if (lbl) {
975         PetscInt val, defval;
976 
977         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
978         PetscCall(PetscBTCreate(pEnd-pStart, &wp));
979         for (c = pStart;  c < pEnd; c++) {
980           PetscInt *closure = NULL;
981           PetscInt  closureSize;
982 
983           PetscCall(DMLabelGetValue(lbl, c, &val));
984           if (val == defval) continue;
985 
986           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
987           for (p = 0; p < closureSize*2; p += 2) {
988             PetscCall(PetscBTSet(wp, closure[p] - pStart));
989           }
990           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
991         }
992       }
993     }
994 
995     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
996     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
997     PetscCall(PetscObjectGetName((PetscObject) dm, &name));
998     PetscCall(PetscViewerASCIIPrintf(viewer, "\
999 \\documentclass[tikz]{standalone}\n\n\
1000 \\usepackage{pgflibraryshapes}\n\
1001 \\usetikzlibrary{backgrounds}\n\
1002 \\usetikzlibrary{arrows}\n\
1003 \\begin{document}\n"));
1004     if (size > 1) {
1005       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1006       for (p = 0; p < size; ++p) {
1007         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size-1) ? ", and " :  ", "));
1008         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p%numColors], p));
1009       }
1010       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1011     }
1012     if (drawHasse) {
1013       PetscInt maxStratum = PetscMax(vEnd-vStart, PetscMax(eEnd-eStart, cEnd-cStart));
1014 
1015       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1016       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd-1));
1017       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd-vStart));
1018       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum-(vEnd-vStart))/2.));
1019       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1020       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd-1));
1021       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum-(eEnd-eStart))/2.));
1022       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd-eStart));
1023       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1024       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd-1));
1025       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd-cStart));
1026       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum-(cEnd-cStart))/2.));
1027     }
1028     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double) tikzscale));
1029 
1030     /* Plot vertices */
1031     PetscCall(VecGetArray(coordinates, &coords));
1032     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1033     for (v = vStart; v < vEnd; ++v) {
1034       PetscInt  off, dof, d;
1035       PetscBool isLabeled = PETSC_FALSE;
1036 
1037       if (wp && !PetscBTLookup(wp,v - pStart)) continue;
1038       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1039       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1040       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1041       PetscCheck(dof <= 3,PETSC_COMM_SELF,PETSC_ERR_PLIB,"coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3",v,dof);
1042       for (d = 0; d < dof; ++d) {
1043         tcoords[d] = (double) (scale*PetscRealPart(coords[off+d]));
1044         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1045       }
1046       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1047       if (dim == 3) {PetscReal tmp = tcoords[1]; tcoords[1] = tcoords[2]; tcoords[2] = -tmp;}
1048       for (d = 0; d < dof; ++d) {
1049         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1050         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double) tcoords[d]));
1051       }
1052       if (drawHasse) color = colors[0%numColors];
1053       else           color = colors[rank%numColors];
1054       for (l = 0; l < numLabels; ++l) {
1055         PetscInt val;
1056         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1057         if (val >= 0) {color = lcolors[l%numLColors]; isLabeled = PETSC_TRUE; break;}
1058       }
1059       if (drawNumbers[0]) {
1060         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1061       } else if (drawColors[0]) {
1062         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1063       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1064     }
1065     PetscCall(VecRestoreArray(coordinates, &coords));
1066     PetscCall(PetscViewerFlush(viewer));
1067     /* Plot edges */
1068     if (plotEdges) {
1069       PetscCall(VecGetArray(coordinates, &coords));
1070       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1071       for (e = eStart; e < eEnd; ++e) {
1072         const PetscInt *cone;
1073         PetscInt        coneSize, offA, offB, dof, d;
1074 
1075         if (wp && !PetscBTLookup(wp,e - pStart)) continue;
1076         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1077         PetscCheck(coneSize == 2,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1078         PetscCall(DMPlexGetCone(dm, e, &cone));
1079         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1080         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1081         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1082         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1083         for (d = 0; d < dof; ++d) {
1084           tcoords[d] = (double) (0.5*scale*PetscRealPart(coords[offA+d]+coords[offB+d]));
1085           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1086         }
1087         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1088         if (dim == 3) {PetscReal tmp = tcoords[1]; tcoords[1] = tcoords[2]; tcoords[2] = -tmp;}
1089         for (d = 0; d < dof; ++d) {
1090           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1091           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1092         }
1093         if (drawHasse) color = colors[1%numColors];
1094         else           color = colors[rank%numColors];
1095         for (l = 0; l < numLabels; ++l) {
1096           PetscInt val;
1097           PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1098           if (val >= 0) {color = lcolors[l%numLColors]; break;}
1099         }
1100         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1101       }
1102       PetscCall(VecRestoreArray(coordinates, &coords));
1103       PetscCall(PetscViewerFlush(viewer));
1104       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1105     }
1106     /* Plot cells */
1107     if (dim == 3 || !drawNumbers[1]) {
1108       for (e = eStart; e < eEnd; ++e) {
1109         const PetscInt *cone;
1110 
1111         if (wp && !PetscBTLookup(wp,e - pStart)) continue;
1112         color = colors[rank%numColors];
1113         for (l = 0; l < numLabels; ++l) {
1114           PetscInt val;
1115           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1116           if (val >= 0) {color = lcolors[l%numLColors]; break;}
1117         }
1118         PetscCall(DMPlexGetCone(dm, e, &cone));
1119         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1120       }
1121     } else {
1122        DMPolytopeType ct;
1123 
1124       /* Drawing a 2D polygon */
1125       for (c = cStart; c < cEnd; ++c) {
1126         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1127         PetscCall(DMPlexGetCellType(dm, c, &ct));
1128         if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR ||
1129             ct == DM_POLYTOPE_TRI_PRISM_TENSOR ||
1130             ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
1131           const PetscInt *cone;
1132           PetscInt        coneSize, e;
1133 
1134           PetscCall(DMPlexGetCone(dm, c, &cone));
1135           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1136           for (e = 0; e < coneSize; ++e) {
1137             const PetscInt *econe;
1138 
1139             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1140             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));
1141           }
1142         } else {
1143           PetscInt *closure = NULL;
1144           PetscInt  closureSize, Nv = 0, v;
1145 
1146           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1147           for (p = 0; p < closureSize*2; p += 2) {
1148             const PetscInt point = closure[p];
1149 
1150             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1151           }
1152           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank%numColors]));
1153           for (v = 0; v <= Nv; ++v) {
1154             const PetscInt vertex = closure[v%Nv];
1155 
1156             if (v > 0) {
1157               if (plotEdges) {
1158                 const PetscInt *edge;
1159                 PetscInt        endpoints[2], ne;
1160 
1161                 endpoints[0] = closure[v-1]; endpoints[1] = vertex;
1162                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1163                 PetscCheck(ne == 1,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1164                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1165                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1166               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1167             }
1168             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1169           }
1170           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1171           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1172         }
1173       }
1174     }
1175     PetscCall(VecGetArray(coordinates, &coords));
1176     for (c = cStart; c < cEnd; ++c) {
1177       double    ccoords[3] = {0.0, 0.0, 0.0};
1178       PetscBool isLabeled  = PETSC_FALSE;
1179       PetscInt *closure    = NULL;
1180       PetscInt  closureSize, dof, d, n = 0;
1181 
1182       if (wp && !PetscBTLookup(wp,c - pStart)) continue;
1183       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1184       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1185       for (p = 0; p < closureSize*2; p += 2) {
1186         const PetscInt point = closure[p];
1187         PetscInt       off;
1188 
1189         if ((point < vStart) || (point >= vEnd)) continue;
1190         PetscCall(PetscSectionGetDof(coordSection, point, &dof));
1191         PetscCall(PetscSectionGetOffset(coordSection, point, &off));
1192         for (d = 0; d < dof; ++d) {
1193           tcoords[d] = (double) (scale*PetscRealPart(coords[off+d]));
1194           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1195         }
1196         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1197         if (dof == 3) {PetscReal tmp = tcoords[1]; tcoords[1] = tcoords[2]; tcoords[2] = -tmp;}
1198         for (d = 0; d < dof; ++d) {ccoords[d] += tcoords[d];}
1199         ++n;
1200       }
1201       for (d = 0; d < dof; ++d) {ccoords[d] /= n;}
1202       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1203       for (d = 0; d < dof; ++d) {
1204         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1205         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double) ccoords[d]));
1206       }
1207       if (drawHasse) color = colors[depth%numColors];
1208       else           color = colors[rank%numColors];
1209       for (l = 0; l < numLabels; ++l) {
1210         PetscInt val;
1211         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1212         if (val >= 0) {color = lcolors[l%numLColors]; isLabeled = PETSC_TRUE; break;}
1213       }
1214       if (drawNumbers[dim]) {
1215         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1216       } else if (drawColors[dim]) {
1217         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1218       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1219     }
1220     PetscCall(VecRestoreArray(coordinates, &coords));
1221     if (drawHasse) {
1222       color = colors[depth%numColors];
1223       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1224       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1225       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1226       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1227       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1228 
1229       color = colors[1%numColors];
1230       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1231       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1232       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1233       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1234       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1235 
1236       color = colors[0%numColors];
1237       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1238       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1239       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1240       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1241       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1242 
1243       for (p = pStart; p < pEnd; ++p) {
1244         const PetscInt *cone;
1245         PetscInt        coneSize, cp;
1246 
1247         PetscCall(DMPlexGetCone(dm, p, &cone));
1248         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1249         for (cp = 0; cp < coneSize; ++cp) {
1250           PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1251         }
1252       }
1253     }
1254     PetscCall(PetscViewerFlush(viewer));
1255     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1256     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1257     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1258     for (l = 0; l < numLabels;  ++l) PetscCall(PetscFree(names[l]));
1259     for (c = 0; c < numColors;  ++c) PetscCall(PetscFree(colors[c]));
1260     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1261     PetscCall(PetscFree3(names, colors, lcolors));
1262     PetscCall(PetscBTDestroy(&wp));
1263   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1264     Vec                    cown,acown;
1265     VecScatter             sct;
1266     ISLocalToGlobalMapping g2l;
1267     IS                     gid,acis;
1268     MPI_Comm               comm,ncomm = MPI_COMM_NULL;
1269     MPI_Group              ggroup,ngroup;
1270     PetscScalar            *array,nid;
1271     const PetscInt         *idxs;
1272     PetscInt               *idxs2,*start,*adjacency,*work;
1273     PetscInt64             lm[3],gm[3];
1274     PetscInt               i,c,cStart,cEnd,cum,numVertices,ect,ectn,cellHeight;
1275     PetscMPIInt            d1,d2,rank;
1276 
1277     PetscCall(PetscObjectGetComm((PetscObject)dm,&comm));
1278     PetscCallMPI(MPI_Comm_rank(comm,&rank));
1279 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1280     PetscCallMPI(MPI_Comm_split_type(comm,MPI_COMM_TYPE_SHARED,rank,MPI_INFO_NULL,&ncomm));
1281 #endif
1282     if (ncomm != MPI_COMM_NULL) {
1283       PetscCallMPI(MPI_Comm_group(comm,&ggroup));
1284       PetscCallMPI(MPI_Comm_group(ncomm,&ngroup));
1285       d1   = 0;
1286       PetscCallMPI(MPI_Group_translate_ranks(ngroup,1,&d1,ggroup,&d2));
1287       nid  = d2;
1288       PetscCallMPI(MPI_Group_free(&ggroup));
1289       PetscCallMPI(MPI_Group_free(&ngroup));
1290       PetscCallMPI(MPI_Comm_free(&ncomm));
1291     } else nid = 0.0;
1292 
1293     /* Get connectivity */
1294     PetscCall(DMPlexGetVTKCellHeight(dm,&cellHeight));
1295     PetscCall(DMPlexCreatePartitionerGraph(dm,cellHeight,&numVertices,&start,&adjacency,&gid));
1296 
1297     /* filter overlapped local cells */
1298     PetscCall(DMPlexGetHeightStratum(dm,cellHeight,&cStart,&cEnd));
1299     PetscCall(ISGetIndices(gid,&idxs));
1300     PetscCall(ISGetLocalSize(gid,&cum));
1301     PetscCall(PetscMalloc1(cum,&idxs2));
1302     for (c = cStart, cum = 0; c < cEnd; c++) {
1303       if (idxs[c-cStart] < 0) continue;
1304       idxs2[cum++] = idxs[c-cStart];
1305     }
1306     PetscCall(ISRestoreIndices(gid,&idxs));
1307     PetscCheck(numVertices == cum,PETSC_COMM_SELF,PETSC_ERR_PLIB,"Unexpected %" PetscInt_FMT " != %" PetscInt_FMT,numVertices,cum);
1308     PetscCall(ISDestroy(&gid));
1309     PetscCall(ISCreateGeneral(comm,numVertices,idxs2,PETSC_OWN_POINTER,&gid));
1310 
1311     /* support for node-aware cell locality */
1312     PetscCall(ISCreateGeneral(comm,start[numVertices],adjacency,PETSC_USE_POINTER,&acis));
1313     PetscCall(VecCreateSeq(PETSC_COMM_SELF,start[numVertices],&acown));
1314     PetscCall(VecCreateMPI(comm,numVertices,PETSC_DECIDE,&cown));
1315     PetscCall(VecGetArray(cown,&array));
1316     for (c = 0; c < numVertices; c++) array[c] = nid;
1317     PetscCall(VecRestoreArray(cown,&array));
1318     PetscCall(VecScatterCreate(cown,acis,acown,NULL,&sct));
1319     PetscCall(VecScatterBegin(sct,cown,acown,INSERT_VALUES,SCATTER_FORWARD));
1320     PetscCall(VecScatterEnd(sct,cown,acown,INSERT_VALUES,SCATTER_FORWARD));
1321     PetscCall(ISDestroy(&acis));
1322     PetscCall(VecScatterDestroy(&sct));
1323     PetscCall(VecDestroy(&cown));
1324 
1325     /* compute edgeCut */
1326     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum,start[c+1]-start[c]);
1327     PetscCall(PetscMalloc1(cum,&work));
1328     PetscCall(ISLocalToGlobalMappingCreateIS(gid,&g2l));
1329     PetscCall(ISLocalToGlobalMappingSetType(g2l,ISLOCALTOGLOBALMAPPINGHASH));
1330     PetscCall(ISDestroy(&gid));
1331     PetscCall(VecGetArray(acown,&array));
1332     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1333       PetscInt totl;
1334 
1335       totl = start[c+1]-start[c];
1336       PetscCall(ISGlobalToLocalMappingApply(g2l,IS_GTOLM_MASK,totl,adjacency+start[c],NULL,work));
1337       for (i = 0; i < totl; i++) {
1338         if (work[i] < 0) {
1339           ect  += 1;
1340           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1341         }
1342       }
1343     }
1344     PetscCall(PetscFree(work));
1345     PetscCall(VecRestoreArray(acown,&array));
1346     lm[0] = numVertices > 0 ?  numVertices : PETSC_MAX_INT;
1347     lm[1] = -numVertices;
1348     PetscCall(MPIU_Allreduce(lm,gm,2,MPIU_INT64,MPI_MIN,comm));
1349     PetscCall(PetscViewerASCIIPrintf(viewer,"  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT,-((double)gm[1])/((double)gm[0]),-(PetscInt)gm[1],(PetscInt)gm[0]));
1350     lm[0] = ect; /* edgeCut */
1351     lm[1] = ectn; /* node-aware edgeCut */
1352     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1353     PetscCall(MPIU_Allreduce(lm,gm,3,MPIU_INT64,MPI_SUM,comm));
1354     PetscCall(PetscViewerASCIIPrintf(viewer,", empty %" PetscInt_FMT ")\n",(PetscInt)gm[2]));
1355 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1356     PetscCall(PetscViewerASCIIPrintf(viewer,"  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n",(PetscInt)(gm[0]/2),gm[0] ? ((double)(gm[1]))/((double)gm[0]) : 1.));
1357 #else
1358     PetscCall(PetscViewerASCIIPrintf(viewer,"  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n",(PetscInt)(gm[0]/2),0.0));
1359 #endif
1360     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1361     PetscCall(PetscFree(start));
1362     PetscCall(PetscFree(adjacency));
1363     PetscCall(VecDestroy(&acown));
1364   } else {
1365     const char    *name;
1366     PetscInt      *sizes, *hybsizes, *ghostsizes;
1367     PetscInt       locDepth, depth, cellHeight, dim, d;
1368     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1369     PetscInt       numLabels, l, maxSize = 17;
1370     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1371     MPI_Comm       comm;
1372     PetscMPIInt    size, rank;
1373 
1374     PetscCall(PetscObjectGetComm((PetscObject) dm, &comm));
1375     PetscCallMPI(MPI_Comm_size(comm, &size));
1376     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1377     PetscCall(DMGetDimension(dm, &dim));
1378     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1379     PetscCall(PetscObjectGetName((PetscObject) dm, &name));
1380     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1381     else      PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1382     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1383     PetscCall(DMPlexGetDepth(dm, &locDepth));
1384     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1385     PetscCall(DMPlexGetGhostCellStratum(dm, &gcStart, &gcEnd));
1386     gcNum = gcEnd - gcStart;
1387     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1388     else                PetscCall(PetscCalloc3(3,    &sizes, 3,    &hybsizes, 3,    &ghostsizes));
1389     for (d = 0; d <= depth; d++) {
1390       PetscInt Nc[2] = {0, 0}, ict;
1391 
1392       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1393       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1394       ict  = ct0;
1395       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1396       ct0  = (DMPolytopeType) ict;
1397       for (p = pStart; p < pEnd; ++p) {
1398         DMPolytopeType ct;
1399 
1400         PetscCall(DMPlexGetCellType(dm, p, &ct));
1401         if (ct == ct0) ++Nc[0];
1402         else           ++Nc[1];
1403       }
1404       if (size < maxSize) {
1405         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes,    1, MPIU_INT, 0, comm));
1406         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1407         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1408         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1409         for (p = 0; p < size; ++p) {
1410           if (rank == 0) {
1411             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p]+hybsizes[p]));
1412             if (hybsizes[p]   > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1413             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1414           }
1415         }
1416       } else {
1417         PetscInt locMinMax[2];
1418 
1419         locMinMax[0] = Nc[0]+Nc[1]; locMinMax[1] = Nc[0]+Nc[1];
1420         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1421         locMinMax[0] = Nc[1]; locMinMax[1] = Nc[1];
1422         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1423         if (d == depth) {
1424           locMinMax[0] = gcNum; locMinMax[1] = gcNum;
1425           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1426         }
1427         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1428         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1429         if (hybsizes[0]   > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1430         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1431       }
1432       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1433     }
1434     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1435     {
1436       const PetscReal *maxCell;
1437       const PetscReal *L;
1438       PetscBool        localized;
1439 
1440       PetscCall(DMGetPeriodicity(dm, &maxCell, &L));
1441       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1442       if (L || localized) {
1443         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1444         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1445         if (L) {
1446           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1447           for (d = 0; d < dim; ++d) {
1448             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1449             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1450           }
1451           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1452         }
1453         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1454         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1455       }
1456     }
1457     PetscCall(DMGetNumLabels(dm, &numLabels));
1458     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1459     for (l = 0; l < numLabels; ++l) {
1460       DMLabel         label;
1461       const char     *name;
1462       IS              valueIS;
1463       const PetscInt *values;
1464       PetscInt        numValues, v;
1465 
1466       PetscCall(DMGetLabelName(dm, l, &name));
1467       PetscCall(DMGetLabel(dm, name, &label));
1468       PetscCall(DMLabelGetNumValues(label, &numValues));
1469       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1470       PetscCall(DMLabelGetValueIS(label, &valueIS));
1471       PetscCall(ISGetIndices(valueIS, &values));
1472       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1473       for (v = 0; v < numValues; ++v) {
1474         PetscInt size;
1475 
1476         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1477         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1478         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1479       }
1480       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1481       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1482       PetscCall(ISRestoreIndices(valueIS, &values));
1483       PetscCall(ISDestroy(&valueIS));
1484     }
1485     {
1486       char    **labelNames;
1487       PetscInt  Nl = numLabels;
1488       PetscBool flg;
1489 
1490       PetscCall(PetscMalloc1(Nl, &labelNames));
1491       PetscCall(PetscOptionsGetStringArray(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1492       for (l = 0; l < Nl; ++l) {
1493         DMLabel label;
1494 
1495         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1496         if (flg) {
1497           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1498           PetscCall(DMLabelView(label, viewer));
1499         }
1500         PetscCall(PetscFree(labelNames[l]));
1501       }
1502       PetscCall(PetscFree(labelNames));
1503     }
1504     /* If no fields are specified, people do not want to see adjacency */
1505     if (dm->Nf) {
1506       PetscInt f;
1507 
1508       for (f = 0; f < dm->Nf; ++f) {
1509         const char *name;
1510 
1511         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1512         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1513         PetscCall(PetscViewerASCIIPushTab(viewer));
1514         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1515         if (dm->fields[f].adjacency[0]) {
1516           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1517           else                            PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1518         } else {
1519           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1520           else                            PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1521         }
1522         PetscCall(PetscViewerASCIIPopTab(viewer));
1523       }
1524     }
1525     PetscCall(DMGetCoarseDM(dm, &cdm));
1526     if (cdm) {
1527       PetscCall(PetscViewerASCIIPushTab(viewer));
1528       PetscCall(DMPlexView_Ascii(cdm, viewer));
1529       PetscCall(PetscViewerASCIIPopTab(viewer));
1530     }
1531   }
1532   PetscFunctionReturn(0);
1533 }
1534 
1535 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1536 {
1537   DMPolytopeType ct;
1538   PetscMPIInt    rank;
1539   PetscInt       cdim;
1540 
1541   PetscFunctionBegin;
1542   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject) dm), &rank));
1543   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1544   PetscCall(DMGetCoordinateDim(dm, &cdim));
1545   switch (ct) {
1546   case DM_POLYTOPE_SEGMENT:
1547   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1548     switch (cdim) {
1549     case 1:
1550     {
1551       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1552       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1553 
1554       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y,    PetscRealPart(coords[1]), y,    PETSC_DRAW_BLACK));
1555       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y+dy, PetscRealPart(coords[0]), y-dy, PETSC_DRAW_BLACK));
1556       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y+dy, PetscRealPart(coords[1]), y-dy, PETSC_DRAW_BLACK));
1557     }
1558     break;
1559     case 2:
1560     {
1561       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1562       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1563       const PetscReal l  = 0.1/PetscSqrtReal(dx*dx + dy*dy);
1564 
1565       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1566       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));
1567       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));
1568     }
1569     break;
1570     default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1571     }
1572     break;
1573   case DM_POLYTOPE_TRIANGLE:
1574     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]),
1575                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1576                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1577                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2));
1578     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1579     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1580     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1581     break;
1582   case DM_POLYTOPE_QUADRILATERAL:
1583     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]),
1584                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1585                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1586                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2));
1587     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]),
1588                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1589                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1590                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2));
1591     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1592     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1593     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1594     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1595     break;
1596   default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1597   }
1598   PetscFunctionReturn(0);
1599 }
1600 
1601 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1602 {
1603   DMPolytopeType ct;
1604   PetscReal      centroid[2] = {0., 0.};
1605   PetscMPIInt    rank;
1606   PetscInt       fillColor, v, e, d;
1607 
1608   PetscFunctionBegin;
1609   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject) dm), &rank));
1610   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1611   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2;
1612   switch (ct) {
1613   case DM_POLYTOPE_TRIANGLE:
1614     {
1615       PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1616 
1617       for (v = 0; v < 3; ++v) {centroid[0] += PetscRealPart(coords[v*2+0])/3.;centroid[1] += PetscRealPart(coords[v*2+1])/3.;}
1618       for (e = 0; e < 3; ++e) {
1619         refCoords[0] = refVertices[e*2+0];
1620         refCoords[1] = refVertices[e*2+1];
1621         for (d = 1; d <= edgeDiv; ++d) {
1622           refCoords[d*2+0] = refCoords[0] + (refVertices[(e+1)%3 * 2 + 0] - refCoords[0])*d/edgeDiv;
1623           refCoords[d*2+1] = refCoords[1] + (refVertices[(e+1)%3 * 2 + 1] - refCoords[1])*d/edgeDiv;
1624         }
1625         PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv+1, refCoords, edgeCoords));
1626         for (d = 0; d < edgeDiv; ++d) {
1627           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));
1628           PetscCall(PetscDrawLine(draw, edgeCoords[d*2+0], edgeCoords[d*2+1], edgeCoords[(d+1)*2+0], edgeCoords[(d+1)*2+1], PETSC_DRAW_BLACK));
1629         }
1630       }
1631     }
1632     break;
1633   default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1634   }
1635   PetscFunctionReturn(0);
1636 }
1637 
1638 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1639 {
1640   PetscDraw          draw;
1641   DM                 cdm;
1642   PetscSection       coordSection;
1643   Vec                coordinates;
1644   const PetscScalar *coords;
1645   PetscReal          xyl[2],xyr[2],bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
1646   PetscReal         *refCoords, *edgeCoords;
1647   PetscBool          isnull, drawAffine = PETSC_TRUE;
1648   PetscInt           dim, vStart, vEnd, cStart, cEnd, c, N, edgeDiv = 4;
1649 
1650   PetscFunctionBegin;
1651   PetscCall(DMGetCoordinateDim(dm, &dim));
1652   PetscCheck(dim <= 2,PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1653   PetscCall(PetscOptionsGetBool(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1654   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv+1)*dim, &refCoords, (edgeDiv+1)*dim, &edgeCoords));
1655   PetscCall(DMGetCoordinateDM(dm, &cdm));
1656   PetscCall(DMGetLocalSection(cdm, &coordSection));
1657   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1658   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1659   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1660 
1661   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1662   PetscCall(PetscDrawIsNull(draw, &isnull));
1663   if (isnull) PetscFunctionReturn(0);
1664   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1665 
1666   PetscCall(VecGetLocalSize(coordinates, &N));
1667   PetscCall(VecGetArrayRead(coordinates, &coords));
1668   for (c = 0; c < N; c += dim) {
1669     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));   bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
1670     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c+1])); bound[3] = PetscMax(bound[3], PetscRealPart(coords[c+1]));
1671   }
1672   PetscCall(VecRestoreArrayRead(coordinates, &coords));
1673   PetscCall(MPIU_Allreduce(&bound[0],xyl,2,MPIU_REAL,MPIU_MIN,PetscObjectComm((PetscObject)dm)));
1674   PetscCall(MPIU_Allreduce(&bound[2],xyr,2,MPIU_REAL,MPIU_MAX,PetscObjectComm((PetscObject)dm)));
1675   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1676   PetscCall(PetscDrawClear(draw));
1677 
1678   for (c = cStart; c < cEnd; ++c) {
1679     PetscScalar *coords = NULL;
1680     PetscInt     numCoords;
1681 
1682     PetscCall(DMPlexVecGetClosureAtDepth_Internal(dm, coordSection, coordinates, c, 0, &numCoords, &coords));
1683     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1684     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1685     PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
1686   }
1687   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1688   PetscCall(PetscDrawFlush(draw));
1689   PetscCall(PetscDrawPause(draw));
1690   PetscCall(PetscDrawSave(draw));
1691   PetscFunctionReturn(0);
1692 }
1693 
1694 #if defined(PETSC_HAVE_EXODUSII)
1695 #include <exodusII.h>
1696 #include <petscviewerexodusii.h>
1697 #endif
1698 
1699 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1700 {
1701   PetscBool      iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus;
1702   char           name[PETSC_MAX_PATH_LEN];
1703 
1704   PetscFunctionBegin;
1705   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1706   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1707   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERASCII,    &iascii));
1708   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,      &isvtk));
1709   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,     &ishdf5));
1710   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERDRAW,     &isdraw));
1711   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERGLVIS,    &isglvis));
1712   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWEREXODUSII, &isexodus));
1713   if (iascii) {
1714     PetscViewerFormat format;
1715     PetscCall(PetscViewerGetFormat(viewer, &format));
1716     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1717     else PetscCall(DMPlexView_Ascii(dm, viewer));
1718   } else if (ishdf5) {
1719 #if defined(PETSC_HAVE_HDF5)
1720     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1721 #else
1722     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1723 #endif
1724   } else if (isvtk) {
1725     PetscCall(DMPlexVTKWriteAll((PetscObject) dm,viewer));
1726   } else if (isdraw) {
1727     PetscCall(DMPlexView_Draw(dm, viewer));
1728   } else if (isglvis) {
1729     PetscCall(DMPlexView_GLVis(dm, viewer));
1730 #if defined(PETSC_HAVE_EXODUSII)
1731   } else if (isexodus) {
1732 /*
1733       exodusII requires that all sets be part of exactly one cell set.
1734       If the dm does not have a "Cell Sets" label defined, we create one
1735       with ID 1, containig all cells.
1736       Note that if the Cell Sets label is defined but does not cover all cells,
1737       we may still have a problem. This should probably be checked here or in the viewer;
1738     */
1739     PetscInt numCS;
1740     PetscCall(DMGetLabelSize(dm,"Cell Sets",&numCS));
1741     if (!numCS) {
1742       PetscInt cStart, cEnd, c;
1743       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1744       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1745       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1746     }
1747     PetscCall(DMView_PlexExodusII(dm, viewer));
1748 #endif
1749   } else SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1750 
1751   /* Optionally view the partition */
1752   PetscCall(PetscOptionsHasName(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_partition_view", &flg));
1753   if (flg) {
1754     Vec ranks;
1755     PetscCall(DMPlexCreateRankField(dm, &ranks));
1756     PetscCall(VecView(ranks, viewer));
1757     PetscCall(VecDestroy(&ranks));
1758   }
1759   /* Optionally view a label */
1760   PetscCall(PetscOptionsGetString(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1761   if (flg) {
1762     DMLabel label;
1763     Vec     val;
1764 
1765     PetscCall(DMGetLabel(dm, name, &label));
1766     PetscCheck(label,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1767     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1768     PetscCall(VecView(val, viewer));
1769     PetscCall(VecDestroy(&val));
1770   }
1771   PetscFunctionReturn(0);
1772 }
1773 
1774 /*@
1775   DMPlexTopologyView - Saves a DMPlex topology into a file
1776 
1777   Collective on DM
1778 
1779   Input Parameters:
1780 + dm     - The DM whose topology is to be saved
1781 - viewer - The PetscViewer for saving
1782 
1783   Level: advanced
1784 
1785 .seealso: `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`
1786 @*/
1787 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
1788 {
1789   PetscBool      ishdf5;
1790 
1791   PetscFunctionBegin;
1792   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1793   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1794   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
1795   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView,viewer,0,0,0));
1796   if (ishdf5) {
1797 #if defined(PETSC_HAVE_HDF5)
1798     PetscViewerFormat format;
1799     PetscCall(PetscViewerGetFormat(viewer, &format));
1800     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1801       IS globalPointNumbering;
1802 
1803       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1804       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
1805       PetscCall(ISDestroy(&globalPointNumbering));
1806     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1807 #else
1808     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1809 #endif
1810   }
1811   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView,viewer,0,0,0));
1812   PetscFunctionReturn(0);
1813 }
1814 
1815 /*@
1816   DMPlexCoordinatesView - Saves DMPlex coordinates into a file
1817 
1818   Collective on DM
1819 
1820   Input Parameters:
1821 + dm     - The DM whose coordinates are to be saved
1822 - viewer - The PetscViewer for saving
1823 
1824   Level: advanced
1825 
1826 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`
1827 @*/
1828 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
1829 {
1830   PetscBool      ishdf5;
1831 
1832   PetscFunctionBegin;
1833   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1834   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1835   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
1836   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView,viewer,0,0,0));
1837   if (ishdf5) {
1838 #if defined(PETSC_HAVE_HDF5)
1839     PetscViewerFormat format;
1840     PetscCall(PetscViewerGetFormat(viewer, &format));
1841     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1842       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
1843     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1844 #else
1845     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1846 #endif
1847   }
1848   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView,viewer,0,0,0));
1849   PetscFunctionReturn(0);
1850 }
1851 
1852 /*@
1853   DMPlexLabelsView - Saves DMPlex labels into a file
1854 
1855   Collective on DM
1856 
1857   Input Parameters:
1858 + dm     - The DM whose labels are to be saved
1859 - viewer - The PetscViewer for saving
1860 
1861   Level: advanced
1862 
1863 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`
1864 @*/
1865 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
1866 {
1867   PetscBool      ishdf5;
1868 
1869   PetscFunctionBegin;
1870   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1871   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1872   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
1873   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView,viewer,0,0,0));
1874   if (ishdf5) {
1875 #if defined(PETSC_HAVE_HDF5)
1876     IS                globalPointNumbering;
1877     PetscViewerFormat format;
1878 
1879     PetscCall(PetscViewerGetFormat(viewer, &format));
1880     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1881       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1882       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
1883       PetscCall(ISDestroy(&globalPointNumbering));
1884     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1885 #else
1886     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1887 #endif
1888   }
1889   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView,viewer,0,0,0));
1890   PetscFunctionReturn(0);
1891 }
1892 
1893 /*@
1894   DMPlexSectionView - Saves a section associated with a DMPlex
1895 
1896   Collective on DM
1897 
1898   Input Parameters:
1899 + dm         - The DM that contains the topology on which the section to be saved is defined
1900 . viewer     - The PetscViewer for saving
1901 - sectiondm  - The DM that contains the section to be saved
1902 
1903   Level: advanced
1904 
1905   Notes:
1906   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.
1907 
1908   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.
1909 
1910 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`
1911 @*/
1912 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
1913 {
1914   PetscBool      ishdf5;
1915 
1916   PetscFunctionBegin;
1917   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1918   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1919   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1920   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
1921   PetscCall(PetscLogEventBegin(DMPLEX_SectionView,viewer,0,0,0));
1922   if (ishdf5) {
1923 #if defined(PETSC_HAVE_HDF5)
1924     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
1925 #else
1926     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1927 #endif
1928   }
1929   PetscCall(PetscLogEventEnd(DMPLEX_SectionView,viewer,0,0,0));
1930   PetscFunctionReturn(0);
1931 }
1932 
1933 /*@
1934   DMPlexGlobalVectorView - Saves a global vector
1935 
1936   Collective on DM
1937 
1938   Input Parameters:
1939 + dm        - The DM that represents the topology
1940 . viewer    - The PetscViewer to save data with
1941 . sectiondm - The DM that contains the global section on which vec is defined
1942 - vec       - The global vector to be saved
1943 
1944   Level: advanced
1945 
1946   Notes:
1947   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.
1948 
1949   Typical calling sequence
1950 $       DMCreate(PETSC_COMM_WORLD, &dm);
1951 $       DMSetType(dm, DMPLEX);
1952 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
1953 $       DMClone(dm, &sectiondm);
1954 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
1955 $       PetscSectionCreate(PETSC_COMM_WORLD, &section);
1956 $       DMPlexGetChart(sectiondm, &pStart, &pEnd);
1957 $       PetscSectionSetChart(section, pStart, pEnd);
1958 $       PetscSectionSetUp(section);
1959 $       DMSetLocalSection(sectiondm, section);
1960 $       PetscSectionDestroy(&section);
1961 $       DMGetGlobalVector(sectiondm, &vec);
1962 $       PetscObjectSetName((PetscObject)vec, "vec_name");
1963 $       DMPlexTopologyView(dm, viewer);
1964 $       DMPlexSectionView(dm, viewer, sectiondm);
1965 $       DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
1966 $       DMRestoreGlobalVector(sectiondm, &vec);
1967 $       DMDestroy(&sectiondm);
1968 $       DMDestroy(&dm);
1969 
1970 .seealso: `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
1971 @*/
1972 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
1973 {
1974   PetscBool       ishdf5;
1975 
1976   PetscFunctionBegin;
1977   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1978   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1979   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1980   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
1981   /* Check consistency */
1982   {
1983     PetscSection  section;
1984     PetscBool     includesConstraints;
1985     PetscInt      m, m1;
1986 
1987     PetscCall(VecGetLocalSize(vec, &m1));
1988     PetscCall(DMGetGlobalSection(sectiondm, &section));
1989     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
1990     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
1991     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
1992     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
1993   }
1994   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1995   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView,viewer,0,0,0));
1996   if (ishdf5) {
1997 #if defined(PETSC_HAVE_HDF5)
1998     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
1999 #else
2000     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2001 #endif
2002   }
2003   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView,viewer,0,0,0));
2004   PetscFunctionReturn(0);
2005 }
2006 
2007 /*@
2008   DMPlexLocalVectorView - Saves a local vector
2009 
2010   Collective on DM
2011 
2012   Input Parameters:
2013 + dm        - The DM that represents the topology
2014 . viewer    - The PetscViewer to save data with
2015 . sectiondm - The DM that contains the local section on which vec is defined; may be the same as dm
2016 - vec       - The local vector to be saved
2017 
2018   Level: advanced
2019 
2020   Notes:
2021   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.
2022 
2023   Typical calling sequence
2024 $       DMCreate(PETSC_COMM_WORLD, &dm);
2025 $       DMSetType(dm, DMPLEX);
2026 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2027 $       DMClone(dm, &sectiondm);
2028 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2029 $       PetscSectionCreate(PETSC_COMM_WORLD, &section);
2030 $       DMPlexGetChart(sectiondm, &pStart, &pEnd);
2031 $       PetscSectionSetChart(section, pStart, pEnd);
2032 $       PetscSectionSetUp(section);
2033 $       DMSetLocalSection(sectiondm, section);
2034 $       DMGetLocalVector(sectiondm, &vec);
2035 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2036 $       DMPlexTopologyView(dm, viewer);
2037 $       DMPlexSectionView(dm, viewer, sectiondm);
2038 $       DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2039 $       DMRestoreLocalVector(sectiondm, &vec);
2040 $       DMDestroy(&sectiondm);
2041 $       DMDestroy(&dm);
2042 
2043 .seealso: `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2044 @*/
2045 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2046 {
2047   PetscBool       ishdf5;
2048 
2049   PetscFunctionBegin;
2050   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2051   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2052   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2053   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2054   /* Check consistency */
2055   {
2056     PetscSection  section;
2057     PetscBool     includesConstraints;
2058     PetscInt      m, m1;
2059 
2060     PetscCall(VecGetLocalSize(vec, &m1));
2061     PetscCall(DMGetLocalSection(sectiondm, &section));
2062     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2063     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2064     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2065     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2066   }
2067   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2068   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView,viewer,0,0,0));
2069   if (ishdf5) {
2070 #if defined(PETSC_HAVE_HDF5)
2071     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2072 #else
2073     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2074 #endif
2075   }
2076   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView,viewer,0,0,0));
2077   PetscFunctionReturn(0);
2078 }
2079 
2080 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2081 {
2082   PetscBool      ishdf5;
2083 
2084   PetscFunctionBegin;
2085   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2086   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2087   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,   &ishdf5));
2088   if (ishdf5) {
2089 #if defined(PETSC_HAVE_HDF5)
2090     PetscViewerFormat format;
2091     PetscCall(PetscViewerGetFormat(viewer, &format));
2092     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2093       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2094     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2095       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2096     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2097     PetscFunctionReturn(0);
2098 #else
2099     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2100 #endif
2101   } else SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2102 }
2103 
2104 /*@
2105   DMPlexTopologyLoad - Loads a topology into a DMPlex
2106 
2107   Collective on DM
2108 
2109   Input Parameters:
2110 + dm     - The DM into which the topology is loaded
2111 - viewer - The PetscViewer for the saved topology
2112 
2113   Output Parameters:
2114 . 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
2115 
2116   Level: advanced
2117 
2118 .seealso: `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2119 @*/
2120 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2121 {
2122   PetscBool      ishdf5;
2123 
2124   PetscFunctionBegin;
2125   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2126   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2127   if (globalToLocalPointSF) PetscValidPointer(globalToLocalPointSF, 3);
2128   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
2129   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad,viewer,0,0,0));
2130   if (ishdf5) {
2131 #if defined(PETSC_HAVE_HDF5)
2132     PetscViewerFormat format;
2133     PetscCall(PetscViewerGetFormat(viewer, &format));
2134     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2135       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2136     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2137 #else
2138     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2139 #endif
2140   }
2141   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad,viewer,0,0,0));
2142   PetscFunctionReturn(0);
2143 }
2144 
2145 /*@
2146   DMPlexCoordinatesLoad - Loads coordinates into a DMPlex
2147 
2148   Collective on DM
2149 
2150   Input Parameters:
2151 + dm     - The DM into which the coordinates are loaded
2152 . viewer - The PetscViewer for the saved coordinates
2153 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2154 
2155   Level: advanced
2156 
2157 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2158 @*/
2159 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2160 {
2161   PetscBool      ishdf5;
2162 
2163   PetscFunctionBegin;
2164   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2165   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2166   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2167   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
2168   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad,viewer,0,0,0));
2169   if (ishdf5) {
2170 #if defined(PETSC_HAVE_HDF5)
2171     PetscViewerFormat format;
2172     PetscCall(PetscViewerGetFormat(viewer, &format));
2173     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2174       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2175     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2176 #else
2177     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2178 #endif
2179   }
2180   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad,viewer,0,0,0));
2181   PetscFunctionReturn(0);
2182 }
2183 
2184 /*@
2185   DMPlexLabelsLoad - Loads labels into a DMPlex
2186 
2187   Collective on DM
2188 
2189   Input Parameters:
2190 + dm     - The DM into which the labels are loaded
2191 . viewer - The PetscViewer for the saved labels
2192 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2193 
2194   Level: advanced
2195 
2196   Notes:
2197   The PetscSF argument must not be NULL if the DM is distributed, otherwise an error occurs.
2198 
2199 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2200 @*/
2201 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2202 {
2203   PetscBool      ishdf5;
2204 
2205   PetscFunctionBegin;
2206   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2207   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2208   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2209   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
2210   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad,viewer,0,0,0));
2211   if (ishdf5) {
2212 #if defined(PETSC_HAVE_HDF5)
2213     PetscViewerFormat format;
2214 
2215     PetscCall(PetscViewerGetFormat(viewer, &format));
2216     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2217       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2218     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2219 #else
2220     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2221 #endif
2222   }
2223   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad,viewer,0,0,0));
2224   PetscFunctionReturn(0);
2225 }
2226 
2227 /*@
2228   DMPlexSectionLoad - Loads section into a DMPlex
2229 
2230   Collective on DM
2231 
2232   Input Parameters:
2233 + dm          - The DM that represents the topology
2234 . viewer      - The PetscViewer that represents the on-disk section (sectionA)
2235 . sectiondm   - The DM into which the on-disk section (sectionA) is migrated
2236 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2237 
2238   Output Parameters
2239 + 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)
2240 - 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)
2241 
2242   Level: advanced
2243 
2244   Notes:
2245   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.
2246 
2247   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.
2248 
2249   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.
2250 
2251   Example using 2 processes:
2252 $  NX (number of points on dm): 4
2253 $  sectionA                   : the on-disk section
2254 $  vecA                       : a vector associated with sectionA
2255 $  sectionB                   : sectiondm's local section constructed in this function
2256 $  vecB (local)               : a vector associated with sectiondm's local section
2257 $  vecB (global)              : a vector associated with sectiondm's global section
2258 $
2259 $                                     rank 0    rank 1
2260 $  vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2261 $  sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2262 $  sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2263 $  sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2264 $  [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2265 $  sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2266 $  sectionB->atlasDof             :     1 0 1 | 1 3
2267 $  sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2268 $  vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2269 $  vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2270 $
2271 $  where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2272 
2273 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`
2274 @*/
2275 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2276 {
2277   PetscBool      ishdf5;
2278 
2279   PetscFunctionBegin;
2280   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2281   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2282   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2283   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2284   if (globalDofSF) PetscValidPointer(globalDofSF, 5);
2285   if (localDofSF) PetscValidPointer(localDofSF, 6);
2286   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
2287   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad,viewer,0,0,0));
2288   if (ishdf5) {
2289 #if defined(PETSC_HAVE_HDF5)
2290     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2291 #else
2292     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2293 #endif
2294   }
2295   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad,viewer,0,0,0));
2296   PetscFunctionReturn(0);
2297 }
2298 
2299 /*@
2300   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2301 
2302   Collective on DM
2303 
2304   Input Parameters:
2305 + dm        - The DM that represents the topology
2306 . viewer    - The PetscViewer that represents the on-disk vector data
2307 . sectiondm - The DM that contains the global section on which vec is defined
2308 . sf        - The SF that migrates the on-disk vector data into vec
2309 - vec       - The global vector to set values of
2310 
2311   Level: advanced
2312 
2313   Notes:
2314   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.
2315 
2316   Typical calling sequence
2317 $       DMCreate(PETSC_COMM_WORLD, &dm);
2318 $       DMSetType(dm, DMPLEX);
2319 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2320 $       DMPlexTopologyLoad(dm, viewer, &sfX);
2321 $       DMClone(dm, &sectiondm);
2322 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2323 $       DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2324 $       DMGetGlobalVector(sectiondm, &vec);
2325 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2326 $       DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2327 $       DMRestoreGlobalVector(sectiondm, &vec);
2328 $       PetscSFDestroy(&gsf);
2329 $       PetscSFDestroy(&sfX);
2330 $       DMDestroy(&sectiondm);
2331 $       DMDestroy(&dm);
2332 
2333 .seealso: `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`
2334 @*/
2335 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2336 {
2337   PetscBool       ishdf5;
2338 
2339   PetscFunctionBegin;
2340   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2341   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2342   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2343   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2344   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2345   /* Check consistency */
2346   {
2347     PetscSection  section;
2348     PetscBool     includesConstraints;
2349     PetscInt      m, m1;
2350 
2351     PetscCall(VecGetLocalSize(vec, &m1));
2352     PetscCall(DMGetGlobalSection(sectiondm, &section));
2353     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2354     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2355     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2356     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2357   }
2358   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
2359   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad,viewer,0,0,0));
2360   if (ishdf5) {
2361 #if defined(PETSC_HAVE_HDF5)
2362     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2363 #else
2364     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2365 #endif
2366   }
2367   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad,viewer,0,0,0));
2368   PetscFunctionReturn(0);
2369 }
2370 
2371 /*@
2372   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2373 
2374   Collective on DM
2375 
2376   Input Parameters:
2377 + dm        - The DM that represents the topology
2378 . viewer    - The PetscViewer that represents the on-disk vector data
2379 . sectiondm - The DM that contains the local section on which vec is defined
2380 . sf        - The SF that migrates the on-disk vector data into vec
2381 - vec       - The local vector to set values of
2382 
2383   Level: advanced
2384 
2385   Notes:
2386   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2387 
2388   Typical calling sequence
2389 $       DMCreate(PETSC_COMM_WORLD, &dm);
2390 $       DMSetType(dm, DMPLEX);
2391 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2392 $       DMPlexTopologyLoad(dm, viewer, &sfX);
2393 $       DMClone(dm, &sectiondm);
2394 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2395 $       DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2396 $       DMGetLocalVector(sectiondm, &vec);
2397 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2398 $       DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2399 $       DMRestoreLocalVector(sectiondm, &vec);
2400 $       PetscSFDestroy(&lsf);
2401 $       PetscSFDestroy(&sfX);
2402 $       DMDestroy(&sectiondm);
2403 $       DMDestroy(&dm);
2404 
2405 .seealso: `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`
2406 @*/
2407 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2408 {
2409   PetscBool       ishdf5;
2410 
2411   PetscFunctionBegin;
2412   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2413   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2414   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2415   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2416   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2417   /* Check consistency */
2418   {
2419     PetscSection  section;
2420     PetscBool     includesConstraints;
2421     PetscInt      m, m1;
2422 
2423     PetscCall(VecGetLocalSize(vec, &m1));
2424     PetscCall(DMGetLocalSection(sectiondm, &section));
2425     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2426     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2427     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2428     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2429   }
2430   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
2431   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad,viewer,0,0,0));
2432   if (ishdf5) {
2433 #if defined(PETSC_HAVE_HDF5)
2434     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2435 #else
2436     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2437 #endif
2438   }
2439   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad,viewer,0,0,0));
2440   PetscFunctionReturn(0);
2441 }
2442 
2443 PetscErrorCode DMDestroy_Plex(DM dm)
2444 {
2445   DM_Plex       *mesh = (DM_Plex*) dm->data;
2446 
2447   PetscFunctionBegin;
2448   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMSetUpGLVisViewer_C",NULL));
2449   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexInsertBoundaryValues_C", NULL));
2450   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMCreateNeumannOverlap_C", NULL));
2451   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMInterpolateSolution_C", NULL));
2452   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexInsertTimeDerviativeBoundaryValues_C", NULL));
2453   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexGetOverlap_C", NULL));
2454   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexDistributeGetDefault_C", NULL));
2455   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexDistributeSetDefault_C", NULL));
2456   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"MatComputeNeumannOverlap_C",NULL));
2457   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexReorderGetDefault_C", NULL));
2458   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexReorderSetDefault_C", NULL));
2459   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexGetOverlap_C",NULL));
2460   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexSetOverlap_C",NULL));
2461   if (--mesh->refct > 0) PetscFunctionReturn(0);
2462   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2463   PetscCall(PetscFree(mesh->cones));
2464   PetscCall(PetscFree(mesh->coneOrientations));
2465   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2466   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2467   PetscCall(PetscFree(mesh->supports));
2468   PetscCall(PetscFree(mesh->facesTmp));
2469   PetscCall(PetscFree(mesh->tetgenOpts));
2470   PetscCall(PetscFree(mesh->triangleOpts));
2471   PetscCall(PetscFree(mesh->transformType));
2472   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2473   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2474   PetscCall(ISDestroy(&mesh->subpointIS));
2475   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2476   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2477   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2478   PetscCall(ISDestroy(&mesh->anchorIS));
2479   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2480   PetscCall(PetscFree(mesh->parents));
2481   PetscCall(PetscFree(mesh->childIDs));
2482   PetscCall(PetscSectionDestroy(&mesh->childSection));
2483   PetscCall(PetscFree(mesh->children));
2484   PetscCall(DMDestroy(&mesh->referenceTree));
2485   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2486   PetscCall(PetscFree(mesh->neighbors));
2487   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2488   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2489   PetscCall(PetscFree(mesh));
2490   PetscFunctionReturn(0);
2491 }
2492 
2493 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2494 {
2495   PetscSection           sectionGlobal;
2496   PetscInt               bs = -1, mbs;
2497   PetscInt               localSize, localStart = 0;
2498   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2499   MatType                mtype;
2500   ISLocalToGlobalMapping ltog;
2501 
2502   PetscFunctionBegin;
2503   PetscCall(MatInitializePackage());
2504   mtype = dm->mattype;
2505   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2506   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2507   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2508   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject) dm)));
2509   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2510   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2511   PetscCall(MatSetType(*J, mtype));
2512   PetscCall(MatSetFromOptions(*J));
2513   PetscCall(MatGetBlockSize(*J, &mbs));
2514   if (mbs > 1) bs = mbs;
2515   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2516   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2517   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2518   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2519   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2520   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2521   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2522   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2523   if (!isShell) {
2524     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2525     PetscInt  *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2526     PetscInt  pStart, pEnd, p, dof, cdof;
2527 
2528     PetscCall(DMGetLocalToGlobalMapping(dm,&ltog));
2529 
2530     PetscCall(PetscCalloc1(localSize, &pblocks));
2531     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2532     for (p = pStart; p < pEnd; ++p) {
2533       PetscInt bdof, offset;
2534 
2535       PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2536       PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2537       PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2538       for (PetscInt i=0; i < dof - cdof; i++)
2539         pblocks[offset - localStart + i] = dof - cdof;
2540       dof  = dof < 0 ? -(dof+1) : dof;
2541       bdof = cdof && (dof-cdof) ? 1 : dof;
2542       if (dof) {
2543         if (bs < 0)          {bs = bdof;}
2544         else if (bs != bdof) {bs = 1;}
2545       }
2546     }
2547     /* Must have same blocksize on all procs (some might have no points) */
2548     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2549     bsLocal[1] = bs;
2550     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject) dm), bsLocal, bsMinMax));
2551     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2552     else bs = bsMinMax[0];
2553     bs = PetscMax(1,bs);
2554     PetscCall(MatSetLocalToGlobalMapping(*J,ltog,ltog));
2555     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2556       PetscCall(MatSetBlockSize(*J, bs));
2557       PetscCall(MatSetUp(*J));
2558     } else {
2559       PetscCall(PetscCalloc4(localSize/bs, &dnz, localSize/bs, &onz, localSize/bs, &dnzu, localSize/bs, &onzu));
2560       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2561       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2562     }
2563     { // Consolidate blocks
2564       PetscInt nblocks = 0;
2565       for (PetscInt i=0; i<localSize; i += PetscMax(1, pblocks[i])) {
2566         if (pblocks[i] == 0) continue;
2567         pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2568         for (PetscInt j=1; j<pblocks[i]; j++) {
2569            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]);
2570         }
2571       }
2572       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2573     }
2574     PetscCall(PetscFree(pblocks));
2575   }
2576   PetscCall(MatSetDM(*J, dm));
2577   PetscFunctionReturn(0);
2578 }
2579 
2580 /*@
2581   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2582 
2583   Not collective
2584 
2585   Input Parameter:
2586 . mesh - The DMPlex
2587 
2588   Output Parameters:
2589 . subsection - The subdomain section
2590 
2591   Level: developer
2592 
2593 .seealso:
2594 @*/
2595 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2596 {
2597   DM_Plex       *mesh = (DM_Plex*) dm->data;
2598 
2599   PetscFunctionBegin;
2600   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2601   if (!mesh->subdomainSection) {
2602     PetscSection section;
2603     PetscSF      sf;
2604 
2605     PetscCall(PetscSFCreate(PETSC_COMM_SELF,&sf));
2606     PetscCall(DMGetLocalSection(dm,&section));
2607     PetscCall(PetscSectionCreateGlobalSection(section,sf,PETSC_FALSE,PETSC_TRUE,&mesh->subdomainSection));
2608     PetscCall(PetscSFDestroy(&sf));
2609   }
2610   *subsection = mesh->subdomainSection;
2611   PetscFunctionReturn(0);
2612 }
2613 
2614 /*@
2615   DMPlexGetChart - Return the interval for all mesh points [pStart, pEnd)
2616 
2617   Not collective
2618 
2619   Input Parameter:
2620 . mesh - The DMPlex
2621 
2622   Output Parameters:
2623 + pStart - The first mesh point
2624 - pEnd   - The upper bound for mesh points
2625 
2626   Level: beginner
2627 
2628 .seealso: `DMPlexCreate()`, `DMPlexSetChart()`
2629 @*/
2630 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2631 {
2632   DM_Plex       *mesh = (DM_Plex*) dm->data;
2633 
2634   PetscFunctionBegin;
2635   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2636   PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2637   PetscFunctionReturn(0);
2638 }
2639 
2640 /*@
2641   DMPlexSetChart - Set the interval for all mesh points [pStart, pEnd)
2642 
2643   Not collective
2644 
2645   Input Parameters:
2646 + mesh - The DMPlex
2647 . pStart - The first mesh point
2648 - pEnd   - The upper bound for mesh points
2649 
2650   Output Parameters:
2651 
2652   Level: beginner
2653 
2654 .seealso: `DMPlexCreate()`, `DMPlexGetChart()`
2655 @*/
2656 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2657 {
2658   DM_Plex       *mesh = (DM_Plex*) dm->data;
2659 
2660   PetscFunctionBegin;
2661   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2662   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2663   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2664   PetscFunctionReturn(0);
2665 }
2666 
2667 /*@
2668   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2669 
2670   Not collective
2671 
2672   Input Parameters:
2673 + mesh - The DMPlex
2674 - p - The point, which must lie in the chart set with DMPlexSetChart()
2675 
2676   Output Parameter:
2677 . size - The cone size for point p
2678 
2679   Level: beginner
2680 
2681 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2682 @*/
2683 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2684 {
2685   DM_Plex       *mesh = (DM_Plex*) dm->data;
2686 
2687   PetscFunctionBegin;
2688   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2689   PetscValidIntPointer(size, 3);
2690   PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2691   PetscFunctionReturn(0);
2692 }
2693 
2694 /*@
2695   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2696 
2697   Not collective
2698 
2699   Input Parameters:
2700 + mesh - The DMPlex
2701 . p - The point, which must lie in the chart set with DMPlexSetChart()
2702 - size - The cone size for point p
2703 
2704   Output Parameter:
2705 
2706   Note:
2707   This should be called after DMPlexSetChart().
2708 
2709   Level: beginner
2710 
2711 .seealso: `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2712 @*/
2713 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
2714 {
2715   DM_Plex       *mesh = (DM_Plex*) dm->data;
2716 
2717   PetscFunctionBegin;
2718   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2719   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
2720   PetscFunctionReturn(0);
2721 }
2722 
2723 /*@
2724   DMPlexAddConeSize - Add the given number of in-edges to this point in the DAG
2725 
2726   Not collective
2727 
2728   Input Parameters:
2729 + mesh - The DMPlex
2730 . p - The point, which must lie in the chart set with DMPlexSetChart()
2731 - size - The additional cone size for point p
2732 
2733   Output Parameter:
2734 
2735   Note:
2736   This should be called after DMPlexSetChart().
2737 
2738   Level: beginner
2739 
2740 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2741 @*/
2742 PetscErrorCode DMPlexAddConeSize(DM dm, PetscInt p, PetscInt size)
2743 {
2744   DM_Plex       *mesh = (DM_Plex*) dm->data;
2745   PetscFunctionBegin;
2746   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2747   PetscCall(PetscSectionAddDof(mesh->coneSection, p, size));
2748   PetscFunctionReturn(0);
2749 }
2750 
2751 /*@C
2752   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
2753 
2754   Not collective
2755 
2756   Input Parameters:
2757 + dm - The DMPlex
2758 - p - The point, which must lie in the chart set with DMPlexSetChart()
2759 
2760   Output Parameter:
2761 . cone - An array of points which are on the in-edges for point p
2762 
2763   Level: beginner
2764 
2765   Fortran Notes:
2766   Since it returns an array, this routine is only available in Fortran 90, and you must
2767   include petsc.h90 in your code.
2768   You must also call DMPlexRestoreCone() after you finish using the returned array.
2769   DMPlexRestoreCone() is not needed/available in C.
2770 
2771 .seealso: `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`
2772 @*/
2773 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
2774 {
2775   DM_Plex       *mesh = (DM_Plex*) dm->data;
2776   PetscInt       off;
2777 
2778   PetscFunctionBegin;
2779   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2780   PetscValidPointer(cone, 3);
2781   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
2782   *cone = &mesh->cones[off];
2783   PetscFunctionReturn(0);
2784 }
2785 
2786 /*@C
2787   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
2788 
2789   Not collective
2790 
2791   Input Parameters:
2792 + dm - The DMPlex
2793 - p - The IS of points, which must lie in the chart set with DMPlexSetChart()
2794 
2795   Output Parameters:
2796 + pConesSection - PetscSection describing the layout of pCones
2797 - pCones - An array of points which are on the in-edges for the point set p
2798 
2799   Level: intermediate
2800 
2801 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`
2802 @*/
2803 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
2804 {
2805   PetscSection        cs, newcs;
2806   PetscInt            *cones;
2807   PetscInt            *newarr=NULL;
2808   PetscInt            n;
2809 
2810   PetscFunctionBegin;
2811   PetscCall(DMPlexGetCones(dm, &cones));
2812   PetscCall(DMPlexGetConeSection(dm, &cs));
2813   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void**)&newarr) : NULL));
2814   if (pConesSection) *pConesSection = newcs;
2815   if (pCones) {
2816     PetscCall(PetscSectionGetStorageSize(newcs, &n));
2817     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
2818   }
2819   PetscFunctionReturn(0);
2820 }
2821 
2822 /*@
2823   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
2824 
2825   Not collective
2826 
2827   Input Parameters:
2828 + dm - The DMPlex
2829 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2830 
2831   Output Parameter:
2832 . expandedPoints - An array of vertices recursively expanded from input points
2833 
2834   Level: advanced
2835 
2836   Notes:
2837   Like DMPlexGetConeRecursive but returns only the 0-depth IS (i.e. vertices only) and no sections.
2838   There is no corresponding Restore function, just call ISDestroy() on the returned IS to deallocate.
2839 
2840 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetDepth()`
2841 @*/
2842 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
2843 {
2844   IS                  *expandedPointsAll;
2845   PetscInt            depth;
2846 
2847   PetscFunctionBegin;
2848   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2849   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2850   PetscValidPointer(expandedPoints, 3);
2851   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2852   *expandedPoints = expandedPointsAll[0];
2853   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
2854   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2855   PetscFunctionReturn(0);
2856 }
2857 
2858 /*@
2859   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).
2860 
2861   Not collective
2862 
2863   Input Parameters:
2864 + dm - The DMPlex
2865 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2866 
2867   Output Parameters:
2868 + depth - (optional) Size of the output arrays, equal to DMPlex depth, returned by DMPlexGetDepth()
2869 . expandedPoints - (optional) An array of index sets with recursively expanded cones
2870 - sections - (optional) An array of sections which describe mappings from points to their cone points
2871 
2872   Level: advanced
2873 
2874   Notes:
2875   Like DMPlexGetConeTuple() but recursive.
2876 
2877   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.
2878   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
2879 
2880   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:
2881   (1) DAG points in expandedPoints[d+1] with depth d+1 to their cone points in expandedPoints[d];
2882   (2) DAG points in expandedPoints[d+1] with depth in [0,d] to the same points in expandedPoints[d].
2883 
2884 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`, `DMPlexGetDepth()`
2885 @*/
2886 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2887 {
2888   const PetscInt      *arr0=NULL, *cone=NULL;
2889   PetscInt            *arr=NULL, *newarr=NULL;
2890   PetscInt            d, depth_, i, n, newn, cn, co, start, end;
2891   IS                  *expandedPoints_;
2892   PetscSection        *sections_;
2893 
2894   PetscFunctionBegin;
2895   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2896   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2897   if (depth) PetscValidIntPointer(depth, 3);
2898   if (expandedPoints) PetscValidPointer(expandedPoints, 4);
2899   if (sections) PetscValidPointer(sections, 5);
2900   PetscCall(ISGetLocalSize(points, &n));
2901   PetscCall(ISGetIndices(points, &arr0));
2902   PetscCall(DMPlexGetDepth(dm, &depth_));
2903   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
2904   PetscCall(PetscCalloc1(depth_, &sections_));
2905   arr = (PetscInt*) arr0; /* this is ok because first generation of arr is not modified */
2906   for (d=depth_-1; d>=0; d--) {
2907     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
2908     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
2909     for (i=0; i<n; i++) {
2910       PetscCall(DMPlexGetDepthStratum(dm, d+1, &start, &end));
2911       if (arr[i] >= start && arr[i] < end) {
2912         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
2913         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
2914       } else {
2915         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
2916       }
2917     }
2918     PetscCall(PetscSectionSetUp(sections_[d]));
2919     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
2920     PetscCall(PetscMalloc1(newn, &newarr));
2921     for (i=0; i<n; i++) {
2922       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
2923       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
2924       if (cn > 1) {
2925         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
2926         PetscCall(PetscMemcpy(&newarr[co], cone, cn*sizeof(PetscInt)));
2927       } else {
2928         newarr[co] = arr[i];
2929       }
2930     }
2931     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
2932     arr = newarr;
2933     n = newn;
2934   }
2935   PetscCall(ISRestoreIndices(points, &arr0));
2936   *depth = depth_;
2937   if (expandedPoints) *expandedPoints = expandedPoints_;
2938   else {
2939     for (d=0; d<depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
2940     PetscCall(PetscFree(expandedPoints_));
2941   }
2942   if (sections) *sections = sections_;
2943   else {
2944     for (d=0; d<depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
2945     PetscCall(PetscFree(sections_));
2946   }
2947   PetscFunctionReturn(0);
2948 }
2949 
2950 /*@
2951   DMPlexRestoreConeRecursive - Deallocates arrays created by DMPlexGetConeRecursive
2952 
2953   Not collective
2954 
2955   Input Parameters:
2956 + dm - The DMPlex
2957 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2958 
2959   Output Parameters:
2960 + depth - (optional) Size of the output arrays, equal to DMPlex depth, returned by DMPlexGetDepth()
2961 . expandedPoints - (optional) An array of recursively expanded cones
2962 - sections - (optional) An array of sections which describe mappings from points to their cone points
2963 
2964   Level: advanced
2965 
2966   Notes:
2967   See DMPlexGetConeRecursive() for details.
2968 
2969 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`, `DMPlexGetDepth()`
2970 @*/
2971 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2972 {
2973   PetscInt            d, depth_;
2974 
2975   PetscFunctionBegin;
2976   PetscCall(DMPlexGetDepth(dm, &depth_));
2977   PetscCheck(!depth || *depth == depth_,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
2978   if (depth) *depth = 0;
2979   if (expandedPoints) {
2980     for (d=0; d<depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
2981     PetscCall(PetscFree(*expandedPoints));
2982   }
2983   if (sections)  {
2984     for (d=0; d<depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
2985     PetscCall(PetscFree(*sections));
2986   }
2987   PetscFunctionReturn(0);
2988 }
2989 
2990 /*@
2991   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
2992 
2993   Not collective
2994 
2995   Input Parameters:
2996 + mesh - The DMPlex
2997 . p - The point, which must lie in the chart set with DMPlexSetChart()
2998 - cone - An array of points which are on the in-edges for point p
2999 
3000   Output Parameter:
3001 
3002   Note:
3003   This should be called after all calls to DMPlexSetConeSize() and DMSetUp().
3004 
3005   Level: beginner
3006 
3007 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3008 @*/
3009 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3010 {
3011   DM_Plex       *mesh = (DM_Plex*) dm->data;
3012   PetscInt       pStart, pEnd;
3013   PetscInt       dof, off, c;
3014 
3015   PetscFunctionBegin;
3016   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3017   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3018   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3019   if (dof) PetscValidIntPointer(cone, 3);
3020   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3021   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);
3022   for (c = 0; c < dof; ++c) {
3023     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);
3024     mesh->cones[off+c] = cone[c];
3025   }
3026   PetscFunctionReturn(0);
3027 }
3028 
3029 /*@C
3030   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3031 
3032   Not collective
3033 
3034   Input Parameters:
3035 + mesh - The DMPlex
3036 - p - The point, which must lie in the chart set with DMPlexSetChart()
3037 
3038   Output Parameter:
3039 . coneOrientation - An array of orientations which are on the in-edges for point p. An orientation is an
3040                     integer giving the prescription for cone traversal.
3041 
3042   Level: beginner
3043 
3044   Notes:
3045   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3046   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3047   of o, however it is not necessarily the inverse. To get the inverse, use DMPolytopeTypeComposeOrientationInv()
3048   with the identity.
3049 
3050   Fortran Notes:
3051   Since it returns an array, this routine is only available in Fortran 90, and you must
3052   include petsc.h90 in your code.
3053   You must also call DMPlexRestoreConeOrientation() after you finish using the returned array.
3054   DMPlexRestoreConeOrientation() is not needed/available in C.
3055 
3056 .seealso: `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3057 @*/
3058 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3059 {
3060   DM_Plex       *mesh = (DM_Plex*) dm->data;
3061   PetscInt       off;
3062 
3063   PetscFunctionBegin;
3064   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3065   if (PetscDefined(USE_DEBUG)) {
3066     PetscInt dof;
3067     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3068     if (dof) PetscValidPointer(coneOrientation, 3);
3069   }
3070   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3071 
3072   *coneOrientation = &mesh->coneOrientations[off];
3073   PetscFunctionReturn(0);
3074 }
3075 
3076 /*@
3077   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3078 
3079   Not collective
3080 
3081   Input Parameters:
3082 + mesh - The DMPlex
3083 . p - The point, which must lie in the chart set with DMPlexSetChart()
3084 - coneOrientation - An array of orientations
3085   Output Parameter:
3086 
3087   Notes:
3088   This should be called after all calls to DMPlexSetConeSize() and DMSetUp().
3089 
3090   The meaning of coneOrientation is detailed in DMPlexGetConeOrientation().
3091 
3092   Level: beginner
3093 
3094 .seealso: `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3095 @*/
3096 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3097 {
3098   DM_Plex       *mesh = (DM_Plex*) dm->data;
3099   PetscInt       pStart, pEnd;
3100   PetscInt       dof, off, c;
3101 
3102   PetscFunctionBegin;
3103   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3104   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3105   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3106   if (dof) PetscValidIntPointer(coneOrientation, 3);
3107   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3108   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);
3109   for (c = 0; c < dof; ++c) {
3110     PetscInt cdof, o = coneOrientation[c];
3111 
3112     PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off+c], &cdof));
3113     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);
3114     mesh->coneOrientations[off+c] = o;
3115   }
3116   PetscFunctionReturn(0);
3117 }
3118 
3119 /*@
3120   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3121 
3122   Not collective
3123 
3124   Input Parameters:
3125 + mesh - The DMPlex
3126 . p - The point, which must lie in the chart set with DMPlexSetChart()
3127 . conePos - The local index in the cone where the point should be put
3128 - conePoint - The mesh point to insert
3129 
3130   Level: beginner
3131 
3132 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3133 @*/
3134 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3135 {
3136   DM_Plex       *mesh = (DM_Plex*) dm->data;
3137   PetscInt       pStart, pEnd;
3138   PetscInt       dof, off;
3139 
3140   PetscFunctionBegin;
3141   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3142   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3143   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);
3144   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);
3145   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3146   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3147   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);
3148   mesh->cones[off+conePos] = conePoint;
3149   PetscFunctionReturn(0);
3150 }
3151 
3152 /*@
3153   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3154 
3155   Not collective
3156 
3157   Input Parameters:
3158 + mesh - The DMPlex
3159 . p - The point, which must lie in the chart set with DMPlexSetChart()
3160 . conePos - The local index in the cone where the point should be put
3161 - coneOrientation - The point orientation to insert
3162 
3163   Level: beginner
3164 
3165   Notes:
3166   The meaning of coneOrientation values is detailed in DMPlexGetConeOrientation().
3167 
3168 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3169 @*/
3170 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3171 {
3172   DM_Plex       *mesh = (DM_Plex*) dm->data;
3173   PetscInt       pStart, pEnd;
3174   PetscInt       dof, off;
3175 
3176   PetscFunctionBegin;
3177   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3178   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3179   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);
3180   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3181   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3182   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);
3183   mesh->coneOrientations[off+conePos] = coneOrientation;
3184   PetscFunctionReturn(0);
3185 }
3186 
3187 /*@
3188   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3189 
3190   Not collective
3191 
3192   Input Parameters:
3193 + mesh - The DMPlex
3194 - p - The point, which must lie in the chart set with DMPlexSetChart()
3195 
3196   Output Parameter:
3197 . size - The support size for point p
3198 
3199   Level: beginner
3200 
3201 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3202 @*/
3203 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3204 {
3205   DM_Plex       *mesh = (DM_Plex*) dm->data;
3206 
3207   PetscFunctionBegin;
3208   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3209   PetscValidIntPointer(size, 3);
3210   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3211   PetscFunctionReturn(0);
3212 }
3213 
3214 /*@
3215   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3216 
3217   Not collective
3218 
3219   Input Parameters:
3220 + mesh - The DMPlex
3221 . p - The point, which must lie in the chart set with DMPlexSetChart()
3222 - size - The support size for point p
3223 
3224   Output Parameter:
3225 
3226   Note:
3227   This should be called after DMPlexSetChart().
3228 
3229   Level: beginner
3230 
3231 .seealso: `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3232 @*/
3233 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3234 {
3235   DM_Plex       *mesh = (DM_Plex*) dm->data;
3236 
3237   PetscFunctionBegin;
3238   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3239   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3240   PetscFunctionReturn(0);
3241 }
3242 
3243 /*@C
3244   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3245 
3246   Not collective
3247 
3248   Input Parameters:
3249 + mesh - The DMPlex
3250 - p - The point, which must lie in the chart set with DMPlexSetChart()
3251 
3252   Output Parameter:
3253 . support - An array of points which are on the out-edges for point p
3254 
3255   Level: beginner
3256 
3257   Fortran Notes:
3258   Since it returns an array, this routine is only available in Fortran 90, and you must
3259   include petsc.h90 in your code.
3260   You must also call DMPlexRestoreSupport() after you finish using the returned array.
3261   DMPlexRestoreSupport() is not needed/available in C.
3262 
3263 .seealso: `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3264 @*/
3265 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3266 {
3267   DM_Plex       *mesh = (DM_Plex*) dm->data;
3268   PetscInt       off;
3269 
3270   PetscFunctionBegin;
3271   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3272   PetscValidPointer(support, 3);
3273   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3274   *support = &mesh->supports[off];
3275   PetscFunctionReturn(0);
3276 }
3277 
3278 /*@
3279   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3280 
3281   Not collective
3282 
3283   Input Parameters:
3284 + mesh - The DMPlex
3285 . p - The point, which must lie in the chart set with DMPlexSetChart()
3286 - support - An array of points which are on the out-edges for point p
3287 
3288   Output Parameter:
3289 
3290   Note:
3291   This should be called after all calls to DMPlexSetSupportSize() and DMSetUp().
3292 
3293   Level: beginner
3294 
3295 .seealso: `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3296 @*/
3297 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3298 {
3299   DM_Plex       *mesh = (DM_Plex*) dm->data;
3300   PetscInt       pStart, pEnd;
3301   PetscInt       dof, off, c;
3302 
3303   PetscFunctionBegin;
3304   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3305   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3306   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3307   if (dof) PetscValidIntPointer(support, 3);
3308   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3309   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);
3310   for (c = 0; c < dof; ++c) {
3311     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);
3312     mesh->supports[off+c] = support[c];
3313   }
3314   PetscFunctionReturn(0);
3315 }
3316 
3317 /*@
3318   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3319 
3320   Not collective
3321 
3322   Input Parameters:
3323 + mesh - The DMPlex
3324 . p - The point, which must lie in the chart set with DMPlexSetChart()
3325 . supportPos - The local index in the cone where the point should be put
3326 - supportPoint - The mesh point to insert
3327 
3328   Level: beginner
3329 
3330 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3331 @*/
3332 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3333 {
3334   DM_Plex       *mesh = (DM_Plex*) dm->data;
3335   PetscInt       pStart, pEnd;
3336   PetscInt       dof, off;
3337 
3338   PetscFunctionBegin;
3339   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3340   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3341   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3342   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3343   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);
3344   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);
3345   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);
3346   mesh->supports[off+supportPos] = supportPoint;
3347   PetscFunctionReturn(0);
3348 }
3349 
3350 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3351 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3352 {
3353   switch (ct) {
3354     case DM_POLYTOPE_SEGMENT:
3355       if (o == -1) return -2;
3356       break;
3357     case DM_POLYTOPE_TRIANGLE:
3358       if (o == -3) return -1;
3359       if (o == -2) return -3;
3360       if (o == -1) return -2;
3361       break;
3362     case DM_POLYTOPE_QUADRILATERAL:
3363       if (o == -4) return -2;
3364       if (o == -3) return -1;
3365       if (o == -2) return -4;
3366       if (o == -1) return -3;
3367       break;
3368     default: return o;
3369   }
3370   return o;
3371 }
3372 
3373 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3374 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3375 {
3376   switch (ct) {
3377     case DM_POLYTOPE_SEGMENT:
3378       if ((o == -2) || (o == 1)) return -1;
3379       if (o == -1) return 0;
3380       break;
3381     case DM_POLYTOPE_TRIANGLE:
3382       if (o == -3) return -2;
3383       if (o == -2) return -1;
3384       if (o == -1) return -3;
3385       break;
3386     case DM_POLYTOPE_QUADRILATERAL:
3387       if (o == -4) return -2;
3388       if (o == -3) return -1;
3389       if (o == -2) return -4;
3390       if (o == -1) return -3;
3391       break;
3392     default: return o;
3393   }
3394   return o;
3395 }
3396 
3397 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3398 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3399 {
3400   PetscInt       pStart, pEnd, p;
3401 
3402   PetscFunctionBegin;
3403   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3404   for (p = pStart; p < pEnd; ++p) {
3405     const PetscInt *cone, *ornt;
3406     PetscInt        coneSize, c;
3407 
3408     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3409     PetscCall(DMPlexGetCone(dm, p, &cone));
3410     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3411     for (c = 0; c < coneSize; ++c) {
3412       DMPolytopeType ct;
3413       const PetscInt o = ornt[c];
3414 
3415       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3416       switch (ct) {
3417         case DM_POLYTOPE_SEGMENT:
3418           if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3419           if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3420           break;
3421         case DM_POLYTOPE_TRIANGLE:
3422           if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3423           if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3424           if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3425           break;
3426         case DM_POLYTOPE_QUADRILATERAL:
3427           if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3428           if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3429           if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3430           if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3431           break;
3432         default: break;
3433       }
3434     }
3435   }
3436   PetscFunctionReturn(0);
3437 }
3438 
3439 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3440 {
3441   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3442   PetscInt       *closure;
3443   const PetscInt *tmp = NULL, *tmpO = NULL;
3444   PetscInt        off = 0, tmpSize, t;
3445 
3446   PetscFunctionBeginHot;
3447   if (ornt) {
3448     PetscCall(DMPlexGetCellType(dm, p, &ct));
3449     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3450   }
3451   if (*points) {
3452     closure = *points;
3453   } else {
3454     PetscInt maxConeSize, maxSupportSize;
3455     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3456     PetscCall(DMGetWorkArray(dm, 2*(PetscMax(maxConeSize, maxSupportSize)+1), MPIU_INT, &closure));
3457   }
3458   if (useCone) {
3459     PetscCall(DMPlexGetConeSize(dm, p, &tmpSize));
3460     PetscCall(DMPlexGetCone(dm, p, &tmp));
3461     PetscCall(DMPlexGetConeOrientation(dm, p, &tmpO));
3462   } else {
3463     PetscCall(DMPlexGetSupportSize(dm, p, &tmpSize));
3464     PetscCall(DMPlexGetSupport(dm, p, &tmp));
3465   }
3466   if (ct == DM_POLYTOPE_UNKNOWN) {
3467     closure[off++] = p;
3468     closure[off++] = 0;
3469     for (t = 0; t < tmpSize; ++t) {
3470       closure[off++] = tmp[t];
3471       closure[off++] = tmpO ? tmpO[t] : 0;
3472     }
3473   } else {
3474     const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, ornt);
3475 
3476     /* We assume that cells with a valid type have faces with a valid type */
3477     closure[off++] = p;
3478     closure[off++] = ornt;
3479     for (t = 0; t < tmpSize; ++t) {
3480       DMPolytopeType ft;
3481 
3482       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3483       closure[off++] = tmp[arr[t]];
3484       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3485     }
3486   }
3487   if (numPoints) *numPoints = tmpSize+1;
3488   if (points)    *points    = closure;
3489   PetscFunctionReturn(0);
3490 }
3491 
3492 /* We need a special tensor verison becasue we want to allow duplicate points in the endcaps for hybrid cells */
3493 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3494 {
3495   const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, o);
3496   const PetscInt *cone, *ornt;
3497   PetscInt       *pts,  *closure = NULL;
3498   DMPolytopeType  ft;
3499   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3500   PetscInt        dim, coneSize, c, d, clSize, cl;
3501 
3502   PetscFunctionBeginHot;
3503   PetscCall(DMGetDimension(dm, &dim));
3504   PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
3505   PetscCall(DMPlexGetCone(dm, point, &cone));
3506   PetscCall(DMPlexGetConeOrientation(dm, point, &ornt));
3507   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3508   coneSeries    = (maxConeSize    > 1) ? ((PetscPowInt(maxConeSize,    dim+1)-1)/(maxConeSize-1))    : dim+1;
3509   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim+1)-1)/(maxSupportSize-1)) : dim+1;
3510   maxSize       = PetscMax(coneSeries, supportSeries);
3511   if (*points) {pts  = *points;}
3512   else         PetscCall(DMGetWorkArray(dm, 2*maxSize, MPIU_INT, &pts));
3513   c    = 0;
3514   pts[c++] = point;
3515   pts[c++] = o;
3516   PetscCall(DMPlexGetCellType(dm, cone[arr[0*2+0]], &ft));
3517   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0*2+0]], DMPolytopeTypeComposeOrientation(ft, arr[0*2+1], ornt[0]), useCone, &clSize, &closure));
3518   for (cl = 0; cl < clSize*2; cl += 2) {pts[c++] = closure[cl]; pts[c++] = closure[cl+1];}
3519   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1*2+0]], DMPolytopeTypeComposeOrientation(ft, arr[1*2+1], ornt[1]), useCone, &clSize, &closure));
3520   for (cl = 0; cl < clSize*2; cl += 2) {pts[c++] = closure[cl]; pts[c++] = closure[cl+1];}
3521   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3522   for (d = 2; d < coneSize; ++d) {
3523     PetscCall(DMPlexGetCellType(dm, cone[arr[d*2+0]], &ft));
3524     pts[c++] = cone[arr[d*2+0]];
3525     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d*2+1], ornt[d]);
3526   }
3527   if (dim >= 3) {
3528     for (d = 2; d < coneSize; ++d) {
3529       const PetscInt  fpoint = cone[arr[d*2+0]];
3530       const PetscInt *fcone, *fornt;
3531       PetscInt        fconeSize, fc, i;
3532 
3533       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3534       const PetscInt *farr = DMPolytopeTypeGetArrangment(ft, DMPolytopeTypeComposeOrientation(ft, arr[d*2+1], ornt[d]));
3535       PetscCall(DMPlexGetConeSize(dm, fpoint, &fconeSize));
3536       PetscCall(DMPlexGetCone(dm, fpoint, &fcone));
3537       PetscCall(DMPlexGetConeOrientation(dm, fpoint, &fornt));
3538       for (fc = 0; fc < fconeSize; ++fc) {
3539         const PetscInt cp = fcone[farr[fc*2+0]];
3540         const PetscInt co = farr[fc*2+1];
3541 
3542         for (i = 0; i < c; i += 2) if (pts[i] == cp) break;
3543         if (i == c) {
3544           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3545           pts[c++] = cp;
3546           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc*2+0]]);
3547         }
3548       }
3549     }
3550   }
3551   *numPoints = c/2;
3552   *points    = pts;
3553   PetscFunctionReturn(0);
3554 }
3555 
3556 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3557 {
3558   DMPolytopeType ct;
3559   PetscInt      *closure, *fifo;
3560   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3561   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3562   PetscInt       depth, maxSize;
3563 
3564   PetscFunctionBeginHot;
3565   PetscCall(DMPlexGetDepth(dm, &depth));
3566   if (depth == 1) {
3567     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3568     PetscFunctionReturn(0);
3569   }
3570   PetscCall(DMPlexGetCellType(dm, p, &ct));
3571   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3572   if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
3573     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3574     PetscFunctionReturn(0);
3575   }
3576   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3577   coneSeries    = (maxConeSize    > 1) ? ((PetscPowInt(maxConeSize,    depth+1)-1)/(maxConeSize-1))    : depth+1;
3578   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth+1)-1)/(maxSupportSize-1)) : depth+1;
3579   maxSize       = PetscMax(coneSeries, supportSeries);
3580   PetscCall(DMGetWorkArray(dm, 3*maxSize, MPIU_INT, &fifo));
3581   if (*points) {closure = *points;}
3582   else         PetscCall(DMGetWorkArray(dm, 2*maxSize, MPIU_INT, &closure));
3583   closure[closureSize++] = p;
3584   closure[closureSize++] = ornt;
3585   fifo[fifoSize++]       = p;
3586   fifo[fifoSize++]       = ornt;
3587   fifo[fifoSize++]       = ct;
3588   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3589   while (fifoSize - fifoStart) {
3590     const PetscInt       q    = fifo[fifoStart++];
3591     const PetscInt       o    = fifo[fifoStart++];
3592     const DMPolytopeType qt   = (DMPolytopeType) fifo[fifoStart++];
3593     const PetscInt      *qarr = DMPolytopeTypeGetArrangment(qt, o);
3594     const PetscInt      *tmp, *tmpO;
3595     PetscInt             tmpSize, t;
3596 
3597     if (PetscDefined(USE_DEBUG)) {
3598       PetscInt nO = DMPolytopeTypeGetNumArrangments(qt)/2;
3599       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);
3600     }
3601     if (useCone) {
3602       PetscCall(DMPlexGetConeSize(dm, q, &tmpSize));
3603       PetscCall(DMPlexGetCone(dm, q, &tmp));
3604       PetscCall(DMPlexGetConeOrientation(dm, q, &tmpO));
3605     } else {
3606       PetscCall(DMPlexGetSupportSize(dm, q, &tmpSize));
3607       PetscCall(DMPlexGetSupport(dm, q, &tmp));
3608       tmpO = NULL;
3609     }
3610     for (t = 0; t < tmpSize; ++t) {
3611       const PetscInt ip = useCone && qarr ? qarr[t*2]   : t;
3612       const PetscInt io = useCone && qarr ? qarr[t*2+1] : 0;
3613       const PetscInt cp = tmp[ip];
3614       PetscCall(DMPlexGetCellType(dm, cp, &ct));
3615       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
3616       PetscInt       c;
3617 
3618       /* Check for duplicate */
3619       for (c = 0; c < closureSize; c += 2) {
3620         if (closure[c] == cp) break;
3621       }
3622       if (c == closureSize) {
3623         closure[closureSize++] = cp;
3624         closure[closureSize++] = co;
3625         fifo[fifoSize++]       = cp;
3626         fifo[fifoSize++]       = co;
3627         fifo[fifoSize++]       = ct;
3628       }
3629     }
3630   }
3631   PetscCall(DMRestoreWorkArray(dm, 3*maxSize, MPIU_INT, &fifo));
3632   if (numPoints) *numPoints = closureSize/2;
3633   if (points)    *points    = closure;
3634   PetscFunctionReturn(0);
3635 }
3636 
3637 /*@C
3638   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
3639 
3640   Not collective
3641 
3642   Input Parameters:
3643 + dm      - The DMPlex
3644 . p       - The mesh point
3645 - useCone - PETSC_TRUE for the closure, otherwise return the star
3646 
3647   Input/Output Parameter:
3648 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
3649            if NULL on input, internal storage will be returned, otherwise the provided array is used
3650 
3651   Output Parameter:
3652 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3653 
3654   Note:
3655   If using internal storage (points is NULL on input), each call overwrites the last output.
3656 
3657   Fortran Notes:
3658   Since it returns an array, this routine is only available in Fortran 90, and you must include petsc.h90 in your code.
3659 
3660   The numPoints argument is not present in the Fortran 90 binding since it is internal to the array.
3661 
3662   Level: beginner
3663 
3664 .seealso: `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3665 @*/
3666 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3667 {
3668   PetscFunctionBeginHot;
3669   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3670   if (numPoints) PetscValidIntPointer(numPoints, 4);
3671   if (points)    PetscValidPointer(points, 5);
3672   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
3673   PetscFunctionReturn(0);
3674 }
3675 
3676 /*@C
3677   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
3678 
3679   Not collective
3680 
3681   Input Parameters:
3682 + dm        - The DMPlex
3683 . p         - The mesh point
3684 . useCone   - PETSC_TRUE for the closure, otherwise return the star
3685 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3686 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
3687 
3688   Note:
3689   If not using internal storage (points is not NULL on input), this call is unnecessary
3690 
3691   Fortran Notes:
3692   Since it returns an array, this routine is only available in Fortran 90, and you must include petsc.h90 in your code.
3693 
3694   The numPoints argument is not present in the Fortran 90 binding since it is internal to the array.
3695 
3696   Level: beginner
3697 
3698 .seealso: `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3699 @*/
3700 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3701 {
3702   PetscFunctionBeginHot;
3703   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3704   if (numPoints) *numPoints = 0;
3705   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
3706   PetscFunctionReturn(0);
3707 }
3708 
3709 /*@
3710   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
3711 
3712   Not collective
3713 
3714   Input Parameter:
3715 . mesh - The DMPlex
3716 
3717   Output Parameters:
3718 + maxConeSize - The maximum number of in-edges
3719 - maxSupportSize - The maximum number of out-edges
3720 
3721   Level: beginner
3722 
3723 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3724 @*/
3725 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
3726 {
3727   DM_Plex *mesh = (DM_Plex*) dm->data;
3728 
3729   PetscFunctionBegin;
3730   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3731   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
3732   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
3733   PetscFunctionReturn(0);
3734 }
3735 
3736 PetscErrorCode DMSetUp_Plex(DM dm)
3737 {
3738   DM_Plex       *mesh = (DM_Plex*) dm->data;
3739   PetscInt       size, maxSupportSize;
3740 
3741   PetscFunctionBegin;
3742   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3743   PetscCall(PetscSectionSetUp(mesh->coneSection));
3744   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
3745   PetscCall(PetscMalloc1(size, &mesh->cones));
3746   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
3747   PetscCall(PetscLogObjectMemory((PetscObject) dm, size*2*sizeof(PetscInt)));
3748   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
3749   if (maxSupportSize) {
3750     PetscCall(PetscSectionSetUp(mesh->supportSection));
3751     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
3752     PetscCall(PetscMalloc1(size, &mesh->supports));
3753     PetscCall(PetscLogObjectMemory((PetscObject) dm, size*sizeof(PetscInt)));
3754   }
3755   PetscFunctionReturn(0);
3756 }
3757 
3758 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
3759 {
3760   PetscFunctionBegin;
3761   if (subdm) PetscCall(DMClone(dm, subdm));
3762   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, is, subdm));
3763   if (subdm) {(*subdm)->useNatural = dm->useNatural;}
3764   if (dm->useNatural && dm->sfMigration) {
3765     PetscSF        sfMigrationInv,sfNatural;
3766     PetscSection   section, sectionSeq;
3767 
3768     (*subdm)->sfMigration = dm->sfMigration;
3769     PetscCall(PetscObjectReference((PetscObject) dm->sfMigration));
3770     PetscCall(DMGetLocalSection((*subdm), &section));
3771     PetscCall(PetscSFCreateInverseSF((*subdm)->sfMigration, &sfMigrationInv));
3772     PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject) (*subdm)), &sectionSeq));
3773     PetscCall(PetscSFDistributeSection(sfMigrationInv, section, NULL, sectionSeq));
3774 
3775     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, sectionSeq, (*subdm)->sfMigration, &sfNatural));
3776     (*subdm)->sfNatural = sfNatural;
3777     PetscCall(PetscSectionDestroy(&sectionSeq));
3778     PetscCall(PetscSFDestroy(&sfMigrationInv));
3779   }
3780   PetscFunctionReturn(0);
3781 }
3782 
3783 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
3784 {
3785   PetscInt       i = 0;
3786 
3787   PetscFunctionBegin;
3788   PetscCall(DMClone(dms[0], superdm));
3789   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
3790   (*superdm)->useNatural = PETSC_FALSE;
3791   for (i = 0; i < len; i++) {
3792     if (dms[i]->useNatural && dms[i]->sfMigration) {
3793       PetscSF        sfMigrationInv,sfNatural;
3794       PetscSection   section, sectionSeq;
3795 
3796       (*superdm)->sfMigration = dms[i]->sfMigration;
3797       PetscCall(PetscObjectReference((PetscObject) dms[i]->sfMigration));
3798       (*superdm)->useNatural = PETSC_TRUE;
3799       PetscCall(DMGetLocalSection((*superdm), &section));
3800       PetscCall(PetscSFCreateInverseSF((*superdm)->sfMigration, &sfMigrationInv));
3801       PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject) (*superdm)), &sectionSeq));
3802       PetscCall(PetscSFDistributeSection(sfMigrationInv, section, NULL, sectionSeq));
3803 
3804       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, sectionSeq, (*superdm)->sfMigration, &sfNatural));
3805       (*superdm)->sfNatural = sfNatural;
3806       PetscCall(PetscSectionDestroy(&sectionSeq));
3807       PetscCall(PetscSFDestroy(&sfMigrationInv));
3808       break;
3809     }
3810   }
3811   PetscFunctionReturn(0);
3812 }
3813 
3814 /*@
3815   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
3816 
3817   Not collective
3818 
3819   Input Parameter:
3820 . mesh - The DMPlex
3821 
3822   Output Parameter:
3823 
3824   Note:
3825   This should be called after all calls to DMPlexSetCone()
3826 
3827   Level: beginner
3828 
3829 .seealso: `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
3830 @*/
3831 PetscErrorCode DMPlexSymmetrize(DM dm)
3832 {
3833   DM_Plex       *mesh = (DM_Plex*) dm->data;
3834   PetscInt      *offsets;
3835   PetscInt       supportSize;
3836   PetscInt       pStart, pEnd, p;
3837 
3838   PetscFunctionBegin;
3839   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3840   PetscCheck(!mesh->supports,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
3841   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize,dm,0,0,0));
3842   /* Calculate support sizes */
3843   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3844   for (p = pStart; p < pEnd; ++p) {
3845     PetscInt dof, off, c;
3846 
3847     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3848     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3849     for (c = off; c < off+dof; ++c) {
3850       PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
3851     }
3852   }
3853   PetscCall(PetscSectionSetUp(mesh->supportSection));
3854   /* Calculate supports */
3855   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
3856   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
3857   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
3858   for (p = pStart; p < pEnd; ++p) {
3859     PetscInt dof, off, c;
3860 
3861     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3862     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3863     for (c = off; c < off+dof; ++c) {
3864       const PetscInt q = mesh->cones[c];
3865       PetscInt       offS;
3866 
3867       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
3868 
3869       mesh->supports[offS+offsets[q]] = p;
3870       ++offsets[q];
3871     }
3872   }
3873   PetscCall(PetscFree(offsets));
3874   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize,dm,0,0,0));
3875   PetscFunctionReturn(0);
3876 }
3877 
3878 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
3879 {
3880   IS             stratumIS;
3881 
3882   PetscFunctionBegin;
3883   if (pStart >= pEnd) PetscFunctionReturn(0);
3884   if (PetscDefined(USE_DEBUG)) {
3885     PetscInt  qStart, qEnd, numLevels, level;
3886     PetscBool overlap = PETSC_FALSE;
3887     PetscCall(DMLabelGetNumValues(label, &numLevels));
3888     for (level = 0; level < numLevels; level++) {
3889       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
3890       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {overlap = PETSC_TRUE; break;}
3891     }
3892     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);
3893   }
3894   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd-pStart, pStart, 1, &stratumIS));
3895   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
3896   PetscCall(ISDestroy(&stratumIS));
3897   PetscFunctionReturn(0);
3898 }
3899 
3900 /*@
3901   DMPlexStratify - The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
3902   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram). The strata group all points of the
3903   same grade, and this function calculates the strata. This grade can be seen as the height (or depth) of the point in
3904   the DAG.
3905 
3906   Collective on dm
3907 
3908   Input Parameter:
3909 . mesh - The DMPlex
3910 
3911   Output Parameter:
3912 
3913   Notes:
3914   Concretely, DMPlexStratify() creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
3915   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
3916   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through DMPlexGetDepthLabel() or DMPlexGetDepthStratum(), or
3917   manually via DMGetLabel().  The height is defined implicitly by height = maxDimension - depth, and can be accessed
3918   via DMPlexGetHeightStratum().  For example, cells have height 0 and faces have height 1.
3919 
3920   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
3921   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
3922   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
3923   to interpolate only that one (e0), so that
3924 $  cone(c0) = {e0, v2}
3925 $  cone(e0) = {v0, v1}
3926   If DMPlexStratify() is run on this mesh, it will give depths
3927 $  depth 0 = {v0, v1, v2}
3928 $  depth 1 = {e0, c0}
3929   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
3930 
3931   DMPlexStratify() should be called after all calls to DMPlexSymmetrize()
3932 
3933   Level: beginner
3934 
3935 .seealso: `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
3936 @*/
3937 PetscErrorCode DMPlexStratify(DM dm)
3938 {
3939   DM_Plex       *mesh = (DM_Plex*) dm->data;
3940   DMLabel        label;
3941   PetscInt       pStart, pEnd, p;
3942   PetscInt       numRoots = 0, numLeaves = 0;
3943 
3944   PetscFunctionBegin;
3945   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3946   PetscCall(PetscLogEventBegin(DMPLEX_Stratify,dm,0,0,0));
3947 
3948   /* Create depth label */
3949   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3950   PetscCall(DMCreateLabel(dm, "depth"));
3951   PetscCall(DMPlexGetDepthLabel(dm, &label));
3952 
3953   {
3954     /* Initialize roots and count leaves */
3955     PetscInt sMin = PETSC_MAX_INT;
3956     PetscInt sMax = PETSC_MIN_INT;
3957     PetscInt coneSize, supportSize;
3958 
3959     for (p = pStart; p < pEnd; ++p) {
3960       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3961       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
3962       if (!coneSize && supportSize) {
3963         sMin = PetscMin(p, sMin);
3964         sMax = PetscMax(p, sMax);
3965         ++numRoots;
3966       } else if (!supportSize && coneSize) {
3967         ++numLeaves;
3968       } else if (!supportSize && !coneSize) {
3969         /* Isolated points */
3970         sMin = PetscMin(p, sMin);
3971         sMax = PetscMax(p, sMax);
3972       }
3973     }
3974     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax+1));
3975   }
3976 
3977   if (numRoots + numLeaves == (pEnd - pStart)) {
3978     PetscInt sMin = PETSC_MAX_INT;
3979     PetscInt sMax = PETSC_MIN_INT;
3980     PetscInt coneSize, supportSize;
3981 
3982     for (p = pStart; p < pEnd; ++p) {
3983       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3984       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
3985       if (!supportSize && coneSize) {
3986         sMin = PetscMin(p, sMin);
3987         sMax = PetscMax(p, sMax);
3988       }
3989     }
3990     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax+1));
3991   } else {
3992     PetscInt level = 0;
3993     PetscInt qStart, qEnd, q;
3994 
3995     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
3996     while (qEnd > qStart) {
3997       PetscInt sMin = PETSC_MAX_INT;
3998       PetscInt sMax = PETSC_MIN_INT;
3999 
4000       for (q = qStart; q < qEnd; ++q) {
4001         const PetscInt *support;
4002         PetscInt        supportSize, s;
4003 
4004         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4005         PetscCall(DMPlexGetSupport(dm, q, &support));
4006         for (s = 0; s < supportSize; ++s) {
4007           sMin = PetscMin(support[s], sMin);
4008           sMax = PetscMax(support[s], sMax);
4009         }
4010       }
4011       PetscCall(DMLabelGetNumValues(label, &level));
4012       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax+1));
4013       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4014     }
4015   }
4016   { /* just in case there is an empty process */
4017     PetscInt numValues, maxValues = 0, v;
4018 
4019     PetscCall(DMLabelGetNumValues(label, &numValues));
4020     PetscCallMPI(MPI_Allreduce(&numValues,&maxValues,1,MPIU_INT,MPI_MAX,PetscObjectComm((PetscObject)dm)));
4021     for (v = numValues; v < maxValues; v++) {
4022       PetscCall(DMLabelAddStratum(label, v));
4023     }
4024   }
4025   PetscCall(PetscObjectStateGet((PetscObject) label, &mesh->depthState));
4026   PetscCall(PetscLogEventEnd(DMPLEX_Stratify,dm,0,0,0));
4027   PetscFunctionReturn(0);
4028 }
4029 
4030 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4031 {
4032   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4033   PetscInt       dim, depth, pheight, coneSize;
4034 
4035   PetscFunctionBeginHot;
4036   PetscCall(DMGetDimension(dm, &dim));
4037   PetscCall(DMPlexGetDepth(dm, &depth));
4038   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4039   pheight = depth - pdepth;
4040   if (depth <= 1) {
4041     switch (pdepth) {
4042       case 0: ct = DM_POLYTOPE_POINT;break;
4043       case 1:
4044         switch (coneSize) {
4045           case 2: ct = DM_POLYTOPE_SEGMENT;break;
4046           case 3: ct = DM_POLYTOPE_TRIANGLE;break;
4047           case 4:
4048           switch (dim) {
4049             case 2: ct = DM_POLYTOPE_QUADRILATERAL;break;
4050             case 3: ct = DM_POLYTOPE_TETRAHEDRON;break;
4051             default: break;
4052           }
4053           break;
4054         case 5: ct = DM_POLYTOPE_PYRAMID;break;
4055         case 6: ct = DM_POLYTOPE_TRI_PRISM_TENSOR;break;
4056         case 8: ct = DM_POLYTOPE_HEXAHEDRON;break;
4057         default: break;
4058       }
4059     }
4060   } else {
4061     if (pdepth == 0) {
4062       ct = DM_POLYTOPE_POINT;
4063     } else if (pheight == 0) {
4064       switch (dim) {
4065         case 1:
4066           switch (coneSize) {
4067             case 2: ct = DM_POLYTOPE_SEGMENT;break;
4068             default: break;
4069           }
4070           break;
4071         case 2:
4072           switch (coneSize) {
4073             case 3: ct = DM_POLYTOPE_TRIANGLE;break;
4074             case 4: ct = DM_POLYTOPE_QUADRILATERAL;break;
4075             default: break;
4076           }
4077           break;
4078         case 3:
4079           switch (coneSize) {
4080             case 4: ct = DM_POLYTOPE_TETRAHEDRON;break;
4081             case 5:
4082             {
4083               const PetscInt *cone;
4084               PetscInt        faceConeSize;
4085 
4086               PetscCall(DMPlexGetCone(dm, p, &cone));
4087               PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4088               switch (faceConeSize) {
4089                 case 3: ct = DM_POLYTOPE_TRI_PRISM_TENSOR;break;
4090                 case 4: ct = DM_POLYTOPE_PYRAMID;break;
4091               }
4092             }
4093             break;
4094             case 6: ct = DM_POLYTOPE_HEXAHEDRON;break;
4095             default: break;
4096           }
4097           break;
4098         default: break;
4099       }
4100     } else if (pheight > 0) {
4101       switch (coneSize) {
4102         case 2: ct = DM_POLYTOPE_SEGMENT;break;
4103         case 3: ct = DM_POLYTOPE_TRIANGLE;break;
4104         case 4: ct = DM_POLYTOPE_QUADRILATERAL;break;
4105         default: break;
4106       }
4107     }
4108   }
4109   *pt = ct;
4110   PetscFunctionReturn(0);
4111 }
4112 
4113 /*@
4114   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4115 
4116   Collective on dm
4117 
4118   Input Parameter:
4119 . mesh - The DMPlex
4120 
4121   DMPlexComputeCellTypes() should be called after all calls to DMPlexSymmetrize() and DMPlexStratify()
4122 
4123   Level: developer
4124 
4125   Note: This function is normally called automatically by Plex when a cell type is requested. It creates an
4126   internal DMLabel named "celltype" which can be directly accessed using DMGetLabel(). A user may disable
4127   automatic creation by creating the label manually, using DMCreateLabel(dm, "celltype").
4128 
4129 .seealso: `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4130 @*/
4131 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4132 {
4133   DM_Plex       *mesh;
4134   DMLabel        ctLabel;
4135   PetscInt       pStart, pEnd, p;
4136 
4137   PetscFunctionBegin;
4138   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4139   mesh = (DM_Plex *) dm->data;
4140   PetscCall(DMCreateLabel(dm, "celltype"));
4141   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4142   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4143   for (p = pStart; p < pEnd; ++p) {
4144     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4145     PetscInt       pdepth;
4146 
4147     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4148     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4149     PetscCheck(ct != DM_POLYTOPE_UNKNOWN,PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " is screwed up", p);
4150     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4151   }
4152   PetscCall(PetscObjectStateGet((PetscObject) ctLabel, &mesh->celltypeState));
4153   PetscCall(PetscObjectViewFromOptions((PetscObject) ctLabel, NULL, "-dm_plex_celltypes_view"));
4154   PetscFunctionReturn(0);
4155 }
4156 
4157 /*@C
4158   DMPlexGetJoin - Get an array for the join of the set of points
4159 
4160   Not Collective
4161 
4162   Input Parameters:
4163 + dm - The DMPlex object
4164 . numPoints - The number of input points for the join
4165 - points - The input points
4166 
4167   Output Parameters:
4168 + numCoveredPoints - The number of points in the join
4169 - coveredPoints - The points in the join
4170 
4171   Level: intermediate
4172 
4173   Note: Currently, this is restricted to a single level join
4174 
4175   Fortran Notes:
4176   Since it returns an array, this routine is only available in Fortran 90, and you must
4177   include petsc.h90 in your code.
4178 
4179   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4180 
4181 .seealso: `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4182 @*/
4183 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4184 {
4185   DM_Plex       *mesh = (DM_Plex*) dm->data;
4186   PetscInt      *join[2];
4187   PetscInt       joinSize, i = 0;
4188   PetscInt       dof, off, p, c, m;
4189   PetscInt       maxSupportSize;
4190 
4191   PetscFunctionBegin;
4192   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4193   PetscValidIntPointer(points, 3);
4194   PetscValidIntPointer(numCoveredPoints, 4);
4195   PetscValidPointer(coveredPoints, 5);
4196   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4197   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4198   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4199   /* Copy in support of first point */
4200   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4201   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4202   for (joinSize = 0; joinSize < dof; ++joinSize) {
4203     join[i][joinSize] = mesh->supports[off+joinSize];
4204   }
4205   /* Check each successive support */
4206   for (p = 1; p < numPoints; ++p) {
4207     PetscInt newJoinSize = 0;
4208 
4209     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4210     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4211     for (c = 0; c < dof; ++c) {
4212       const PetscInt point = mesh->supports[off+c];
4213 
4214       for (m = 0; m < joinSize; ++m) {
4215         if (point == join[i][m]) {
4216           join[1-i][newJoinSize++] = point;
4217           break;
4218         }
4219       }
4220     }
4221     joinSize = newJoinSize;
4222     i        = 1-i;
4223   }
4224   *numCoveredPoints = joinSize;
4225   *coveredPoints    = join[i];
4226   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1-i]));
4227   PetscFunctionReturn(0);
4228 }
4229 
4230 /*@C
4231   DMPlexRestoreJoin - Restore an array for the join of the set of points
4232 
4233   Not Collective
4234 
4235   Input Parameters:
4236 + dm - The DMPlex object
4237 . numPoints - The number of input points for the join
4238 - points - The input points
4239 
4240   Output Parameters:
4241 + numCoveredPoints - The number of points in the join
4242 - coveredPoints - The points in the join
4243 
4244   Fortran Notes:
4245   Since it returns an array, this routine is only available in Fortran 90, and you must
4246   include petsc.h90 in your code.
4247 
4248   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4249 
4250   Level: intermediate
4251 
4252 .seealso: `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4253 @*/
4254 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4255 {
4256   PetscFunctionBegin;
4257   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4258   if (points) PetscValidIntPointer(points,3);
4259   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints,4);
4260   PetscValidPointer(coveredPoints, 5);
4261   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void*) coveredPoints));
4262   if (numCoveredPoints) *numCoveredPoints = 0;
4263   PetscFunctionReturn(0);
4264 }
4265 
4266 /*@C
4267   DMPlexGetFullJoin - Get an array for the join of the set of points
4268 
4269   Not Collective
4270 
4271   Input Parameters:
4272 + dm - The DMPlex object
4273 . numPoints - The number of input points for the join
4274 - points - The input points
4275 
4276   Output Parameters:
4277 + numCoveredPoints - The number of points in the join
4278 - coveredPoints - The points in the join
4279 
4280   Fortran Notes:
4281   Since it returns an array, this routine is only available in Fortran 90, and you must
4282   include petsc.h90 in your code.
4283 
4284   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4285 
4286   Level: intermediate
4287 
4288 .seealso: `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4289 @*/
4290 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4291 {
4292   PetscInt      *offsets, **closures;
4293   PetscInt      *join[2];
4294   PetscInt       depth = 0, maxSize, joinSize = 0, i = 0;
4295   PetscInt       p, d, c, m, ms;
4296 
4297   PetscFunctionBegin;
4298   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4299   PetscValidIntPointer(points, 3);
4300   PetscValidIntPointer(numCoveredPoints, 4);
4301   PetscValidPointer(coveredPoints, 5);
4302 
4303   PetscCall(DMPlexGetDepth(dm, &depth));
4304   PetscCall(PetscCalloc1(numPoints, &closures));
4305   PetscCall(DMGetWorkArray(dm, numPoints*(depth+2), MPIU_INT, &offsets));
4306   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4307   maxSize = (ms > 1) ? ((PetscPowInt(ms,depth+1)-1)/(ms-1)) : depth + 1;
4308   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4309   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4310 
4311   for (p = 0; p < numPoints; ++p) {
4312     PetscInt closureSize;
4313 
4314     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4315 
4316     offsets[p*(depth+2)+0] = 0;
4317     for (d = 0; d < depth+1; ++d) {
4318       PetscInt pStart, pEnd, i;
4319 
4320       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4321       for (i = offsets[p*(depth+2)+d]; i < closureSize; ++i) {
4322         if ((pStart > closures[p][i*2]) || (pEnd <= closures[p][i*2])) {
4323           offsets[p*(depth+2)+d+1] = i;
4324           break;
4325         }
4326       }
4327       if (i == closureSize) offsets[p*(depth+2)+d+1] = i;
4328     }
4329     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);
4330   }
4331   for (d = 0; d < depth+1; ++d) {
4332     PetscInt dof;
4333 
4334     /* Copy in support of first point */
4335     dof = offsets[d+1] - offsets[d];
4336     for (joinSize = 0; joinSize < dof; ++joinSize) {
4337       join[i][joinSize] = closures[0][(offsets[d]+joinSize)*2];
4338     }
4339     /* Check each successive cone */
4340     for (p = 1; p < numPoints && joinSize; ++p) {
4341       PetscInt newJoinSize = 0;
4342 
4343       dof = offsets[p*(depth+2)+d+1] - offsets[p*(depth+2)+d];
4344       for (c = 0; c < dof; ++c) {
4345         const PetscInt point = closures[p][(offsets[p*(depth+2)+d]+c)*2];
4346 
4347         for (m = 0; m < joinSize; ++m) {
4348           if (point == join[i][m]) {
4349             join[1-i][newJoinSize++] = point;
4350             break;
4351           }
4352         }
4353       }
4354       joinSize = newJoinSize;
4355       i        = 1-i;
4356     }
4357     if (joinSize) break;
4358   }
4359   *numCoveredPoints = joinSize;
4360   *coveredPoints    = join[i];
4361   for (p = 0; p < numPoints; ++p) {
4362     PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4363   }
4364   PetscCall(PetscFree(closures));
4365   PetscCall(DMRestoreWorkArray(dm, numPoints*(depth+2), MPIU_INT, &offsets));
4366   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1-i]));
4367   PetscFunctionReturn(0);
4368 }
4369 
4370 /*@C
4371   DMPlexGetMeet - Get an array for the meet of the set of points
4372 
4373   Not Collective
4374 
4375   Input Parameters:
4376 + dm - The DMPlex object
4377 . numPoints - The number of input points for the meet
4378 - points - The input points
4379 
4380   Output Parameters:
4381 + numCoveredPoints - The number of points in the meet
4382 - coveredPoints - The points in the meet
4383 
4384   Level: intermediate
4385 
4386   Note: Currently, this is restricted to a single level meet
4387 
4388   Fortran Notes:
4389   Since it returns an array, this routine is only available in Fortran 90, and you must
4390   include petsc.h90 in your code.
4391 
4392   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4393 
4394 .seealso: `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4395 @*/
4396 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4397 {
4398   DM_Plex       *mesh = (DM_Plex*) dm->data;
4399   PetscInt      *meet[2];
4400   PetscInt       meetSize, i = 0;
4401   PetscInt       dof, off, p, c, m;
4402   PetscInt       maxConeSize;
4403 
4404   PetscFunctionBegin;
4405   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4406   PetscValidIntPointer(points, 3);
4407   PetscValidIntPointer(numCoveringPoints, 4);
4408   PetscValidPointer(coveringPoints, 5);
4409   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4410   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4411   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4412   /* Copy in cone of first point */
4413   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4414   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4415   for (meetSize = 0; meetSize < dof; ++meetSize) {
4416     meet[i][meetSize] = mesh->cones[off+meetSize];
4417   }
4418   /* Check each successive cone */
4419   for (p = 1; p < numPoints; ++p) {
4420     PetscInt newMeetSize = 0;
4421 
4422     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4423     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4424     for (c = 0; c < dof; ++c) {
4425       const PetscInt point = mesh->cones[off+c];
4426 
4427       for (m = 0; m < meetSize; ++m) {
4428         if (point == meet[i][m]) {
4429           meet[1-i][newMeetSize++] = point;
4430           break;
4431         }
4432       }
4433     }
4434     meetSize = newMeetSize;
4435     i        = 1-i;
4436   }
4437   *numCoveringPoints = meetSize;
4438   *coveringPoints    = meet[i];
4439   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1-i]));
4440   PetscFunctionReturn(0);
4441 }
4442 
4443 /*@C
4444   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4445 
4446   Not Collective
4447 
4448   Input Parameters:
4449 + dm - The DMPlex object
4450 . numPoints - The number of input points for the meet
4451 - points - The input points
4452 
4453   Output Parameters:
4454 + numCoveredPoints - The number of points in the meet
4455 - coveredPoints - The points in the meet
4456 
4457   Level: intermediate
4458 
4459   Fortran Notes:
4460   Since it returns an array, this routine is only available in Fortran 90, and you must
4461   include petsc.h90 in your code.
4462 
4463   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4464 
4465 .seealso: `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4466 @*/
4467 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4468 {
4469   PetscFunctionBegin;
4470   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4471   if (points) PetscValidIntPointer(points,3);
4472   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints,4);
4473   PetscValidPointer(coveredPoints,5);
4474   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void*) coveredPoints));
4475   if (numCoveredPoints) *numCoveredPoints = 0;
4476   PetscFunctionReturn(0);
4477 }
4478 
4479 /*@C
4480   DMPlexGetFullMeet - Get an array for the meet of the set of points
4481 
4482   Not Collective
4483 
4484   Input Parameters:
4485 + dm - The DMPlex object
4486 . numPoints - The number of input points for the meet
4487 - points - The input points
4488 
4489   Output Parameters:
4490 + numCoveredPoints - The number of points in the meet
4491 - coveredPoints - The points in the meet
4492 
4493   Level: intermediate
4494 
4495   Fortran Notes:
4496   Since it returns an array, this routine is only available in Fortran 90, and you must
4497   include petsc.h90 in your code.
4498 
4499   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4500 
4501 .seealso: `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4502 @*/
4503 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4504 {
4505   PetscInt      *offsets, **closures;
4506   PetscInt      *meet[2];
4507   PetscInt       height = 0, maxSize, meetSize = 0, i = 0;
4508   PetscInt       p, h, c, m, mc;
4509 
4510   PetscFunctionBegin;
4511   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4512   PetscValidIntPointer(points, 3);
4513   PetscValidIntPointer(numCoveredPoints, 4);
4514   PetscValidPointer(coveredPoints, 5);
4515 
4516   PetscCall(DMPlexGetDepth(dm, &height));
4517   PetscCall(PetscMalloc1(numPoints, &closures));
4518   PetscCall(DMGetWorkArray(dm, numPoints*(height+2), MPIU_INT, &offsets));
4519   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4520   maxSize = (mc > 1) ? ((PetscPowInt(mc,height+1)-1)/(mc-1)) : height + 1;
4521   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4522   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4523 
4524   for (p = 0; p < numPoints; ++p) {
4525     PetscInt closureSize;
4526 
4527     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4528 
4529     offsets[p*(height+2)+0] = 0;
4530     for (h = 0; h < height+1; ++h) {
4531       PetscInt pStart, pEnd, i;
4532 
4533       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4534       for (i = offsets[p*(height+2)+h]; i < closureSize; ++i) {
4535         if ((pStart > closures[p][i*2]) || (pEnd <= closures[p][i*2])) {
4536           offsets[p*(height+2)+h+1] = i;
4537           break;
4538         }
4539       }
4540       if (i == closureSize) offsets[p*(height+2)+h+1] = i;
4541     }
4542     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);
4543   }
4544   for (h = 0; h < height+1; ++h) {
4545     PetscInt dof;
4546 
4547     /* Copy in cone of first point */
4548     dof = offsets[h+1] - offsets[h];
4549     for (meetSize = 0; meetSize < dof; ++meetSize) {
4550       meet[i][meetSize] = closures[0][(offsets[h]+meetSize)*2];
4551     }
4552     /* Check each successive cone */
4553     for (p = 1; p < numPoints && meetSize; ++p) {
4554       PetscInt newMeetSize = 0;
4555 
4556       dof = offsets[p*(height+2)+h+1] - offsets[p*(height+2)+h];
4557       for (c = 0; c < dof; ++c) {
4558         const PetscInt point = closures[p][(offsets[p*(height+2)+h]+c)*2];
4559 
4560         for (m = 0; m < meetSize; ++m) {
4561           if (point == meet[i][m]) {
4562             meet[1-i][newMeetSize++] = point;
4563             break;
4564           }
4565         }
4566       }
4567       meetSize = newMeetSize;
4568       i        = 1-i;
4569     }
4570     if (meetSize) break;
4571   }
4572   *numCoveredPoints = meetSize;
4573   *coveredPoints    = meet[i];
4574   for (p = 0; p < numPoints; ++p) {
4575     PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
4576   }
4577   PetscCall(PetscFree(closures));
4578   PetscCall(DMRestoreWorkArray(dm, numPoints*(height+2), MPIU_INT, &offsets));
4579   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1-i]));
4580   PetscFunctionReturn(0);
4581 }
4582 
4583 /*@C
4584   DMPlexEqual - Determine if two DMs have the same topology
4585 
4586   Not Collective
4587 
4588   Input Parameters:
4589 + dmA - A DMPlex object
4590 - dmB - A DMPlex object
4591 
4592   Output Parameters:
4593 . equal - PETSC_TRUE if the topologies are identical
4594 
4595   Level: intermediate
4596 
4597   Notes:
4598   We are not solving graph isomorphism, so we do not permutation.
4599 
4600 .seealso: `DMPlexGetCone()`
4601 @*/
4602 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
4603 {
4604   PetscInt       depth, depthB, pStart, pEnd, pStartB, pEndB, p;
4605 
4606   PetscFunctionBegin;
4607   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
4608   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
4609   PetscValidBoolPointer(equal, 3);
4610 
4611   *equal = PETSC_FALSE;
4612   PetscCall(DMPlexGetDepth(dmA, &depth));
4613   PetscCall(DMPlexGetDepth(dmB, &depthB));
4614   if (depth != depthB) PetscFunctionReturn(0);
4615   PetscCall(DMPlexGetChart(dmA, &pStart,  &pEnd));
4616   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
4617   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(0);
4618   for (p = pStart; p < pEnd; ++p) {
4619     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
4620     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
4621 
4622     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
4623     PetscCall(DMPlexGetCone(dmA, p, &cone));
4624     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
4625     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
4626     PetscCall(DMPlexGetCone(dmB, p, &coneB));
4627     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
4628     if (coneSize != coneSizeB) PetscFunctionReturn(0);
4629     for (c = 0; c < coneSize; ++c) {
4630       if (cone[c] != coneB[c]) PetscFunctionReturn(0);
4631       if (ornt[c] != orntB[c]) PetscFunctionReturn(0);
4632     }
4633     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
4634     PetscCall(DMPlexGetSupport(dmA, p, &support));
4635     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
4636     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
4637     if (supportSize != supportSizeB) PetscFunctionReturn(0);
4638     for (s = 0; s < supportSize; ++s) {
4639       if (support[s] != supportB[s]) PetscFunctionReturn(0);
4640     }
4641   }
4642   *equal = PETSC_TRUE;
4643   PetscFunctionReturn(0);
4644 }
4645 
4646 /*@C
4647   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
4648 
4649   Not Collective
4650 
4651   Input Parameters:
4652 + dm         - The DMPlex
4653 . cellDim    - The cell dimension
4654 - numCorners - The number of vertices on a cell
4655 
4656   Output Parameters:
4657 . numFaceVertices - The number of vertices on a face
4658 
4659   Level: developer
4660 
4661   Notes:
4662   Of course this can only work for a restricted set of symmetric shapes
4663 
4664 .seealso: `DMPlexGetCone()`
4665 @*/
4666 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
4667 {
4668   MPI_Comm       comm;
4669 
4670   PetscFunctionBegin;
4671   PetscCall(PetscObjectGetComm((PetscObject)dm,&comm));
4672   PetscValidIntPointer(numFaceVertices,4);
4673   switch (cellDim) {
4674   case 0:
4675     *numFaceVertices = 0;
4676     break;
4677   case 1:
4678     *numFaceVertices = 1;
4679     break;
4680   case 2:
4681     switch (numCorners) {
4682     case 3: /* triangle */
4683       *numFaceVertices = 2; /* Edge has 2 vertices */
4684       break;
4685     case 4: /* quadrilateral */
4686       *numFaceVertices = 2; /* Edge has 2 vertices */
4687       break;
4688     case 6: /* quadratic triangle, tri and quad cohesive Lagrange cells */
4689       *numFaceVertices = 3; /* Edge has 3 vertices */
4690       break;
4691     case 9: /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
4692       *numFaceVertices = 3; /* Edge has 3 vertices */
4693       break;
4694     default:
4695       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4696     }
4697     break;
4698   case 3:
4699     switch (numCorners) {
4700     case 4: /* tetradehdron */
4701       *numFaceVertices = 3; /* Face has 3 vertices */
4702       break;
4703     case 6: /* tet cohesive cells */
4704       *numFaceVertices = 4; /* Face has 4 vertices */
4705       break;
4706     case 8: /* hexahedron */
4707       *numFaceVertices = 4; /* Face has 4 vertices */
4708       break;
4709     case 9: /* tet cohesive Lagrange cells */
4710       *numFaceVertices = 6; /* Face has 6 vertices */
4711       break;
4712     case 10: /* quadratic tetrahedron */
4713       *numFaceVertices = 6; /* Face has 6 vertices */
4714       break;
4715     case 12: /* hex cohesive Lagrange cells */
4716       *numFaceVertices = 6; /* Face has 6 vertices */
4717       break;
4718     case 18: /* quadratic tet cohesive Lagrange cells */
4719       *numFaceVertices = 6; /* Face has 6 vertices */
4720       break;
4721     case 27: /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
4722       *numFaceVertices = 9; /* Face has 9 vertices */
4723       break;
4724     default:
4725       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4726     }
4727     break;
4728   default:
4729     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
4730   }
4731   PetscFunctionReturn(0);
4732 }
4733 
4734 /*@
4735   DMPlexGetDepthLabel - Get the DMLabel recording the depth of each point
4736 
4737   Not Collective
4738 
4739   Input Parameter:
4740 . dm    - The DMPlex object
4741 
4742   Output Parameter:
4743 . depthLabel - The DMLabel recording point depth
4744 
4745   Level: developer
4746 
4747 .seealso: `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
4748 @*/
4749 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
4750 {
4751   PetscFunctionBegin;
4752   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4753   PetscValidPointer(depthLabel, 2);
4754   *depthLabel = dm->depthLabel;
4755   PetscFunctionReturn(0);
4756 }
4757 
4758 /*@
4759   DMPlexGetDepth - Get the depth of the DAG representing this mesh
4760 
4761   Not Collective
4762 
4763   Input Parameter:
4764 . dm    - The DMPlex object
4765 
4766   Output Parameter:
4767 . depth - The number of strata (breadth first levels) in the DAG
4768 
4769   Level: developer
4770 
4771   Notes:
4772   This returns maximum of point depths over all points, i.e. maximum value of the label returned by DMPlexGetDepthLabel().
4773   The point depth is described more in detail in DMPlexGetDepthStratum().
4774   An empty mesh gives -1.
4775 
4776 .seealso: `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
4777 @*/
4778 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
4779 {
4780   DMLabel        label;
4781   PetscInt       d = 0;
4782 
4783   PetscFunctionBegin;
4784   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4785   PetscValidIntPointer(depth, 2);
4786   PetscCall(DMPlexGetDepthLabel(dm, &label));
4787   if (label) PetscCall(DMLabelGetNumValues(label, &d));
4788   *depth = d-1;
4789   PetscFunctionReturn(0);
4790 }
4791 
4792 /*@
4793   DMPlexGetDepthStratum - Get the bounds [start, end) for all points at a certain depth.
4794 
4795   Not Collective
4796 
4797   Input Parameters:
4798 + dm    - The DMPlex object
4799 - depth - The requested depth
4800 
4801   Output Parameters:
4802 + start - The first point at this depth
4803 - end   - One beyond the last point at this depth
4804 
4805   Notes:
4806   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
4807   often "vertices".  If the mesh is "interpolated" (see DMPlexInterpolate()), then depth stratum 1 contains the next
4808   higher dimension, e.g., "edges".
4809 
4810   Level: developer
4811 
4812 .seealso: `DMPlexGetHeightStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
4813 @*/
4814 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
4815 {
4816   DMLabel        label;
4817   PetscInt       pStart, pEnd;
4818 
4819   PetscFunctionBegin;
4820   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4821   if (start) {PetscValidIntPointer(start, 3); *start = 0;}
4822   if (end)   {PetscValidIntPointer(end,   4); *end   = 0;}
4823   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4824   if (pStart == pEnd) PetscFunctionReturn(0);
4825   if (depth < 0) {
4826     if (start) *start = pStart;
4827     if (end)   *end   = pEnd;
4828     PetscFunctionReturn(0);
4829   }
4830   PetscCall(DMPlexGetDepthLabel(dm, &label));
4831   PetscCheck(label,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
4832   PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
4833   PetscFunctionReturn(0);
4834 }
4835 
4836 /*@
4837   DMPlexGetHeightStratum - Get the bounds [start, end) for all points at a certain height.
4838 
4839   Not Collective
4840 
4841   Input Parameters:
4842 + dm     - The DMPlex object
4843 - height - The requested height
4844 
4845   Output Parameters:
4846 + start - The first point at this height
4847 - end   - One beyond the last point at this height
4848 
4849   Notes:
4850   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
4851   points, often called "cells" or "elements".  If the mesh is "interpolated" (see DMPlexInterpolate()), then height
4852   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
4853 
4854   Level: developer
4855 
4856 .seealso: `DMPlexGetDepthStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
4857 @*/
4858 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
4859 {
4860   DMLabel        label;
4861   PetscInt       depth, pStart, pEnd;
4862 
4863   PetscFunctionBegin;
4864   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4865   if (start) {PetscValidIntPointer(start, 3); *start = 0;}
4866   if (end)   {PetscValidIntPointer(end,   4); *end   = 0;}
4867   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4868   if (pStart == pEnd) PetscFunctionReturn(0);
4869   if (height < 0) {
4870     if (start) *start = pStart;
4871     if (end)   *end   = pEnd;
4872     PetscFunctionReturn(0);
4873   }
4874   PetscCall(DMPlexGetDepthLabel(dm, &label));
4875   PetscCheck(label,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
4876   PetscCall(DMLabelGetNumValues(label, &depth));
4877   PetscCall(DMLabelGetStratumBounds(label, depth-1-height, start, end));
4878   PetscFunctionReturn(0);
4879 }
4880 
4881 /*@
4882   DMPlexGetPointDepth - Get the depth of a given point
4883 
4884   Not Collective
4885 
4886   Input Parameters:
4887 + dm    - The DMPlex object
4888 - point - The point
4889 
4890   Output Parameter:
4891 . depth - The depth of the point
4892 
4893   Level: intermediate
4894 
4895 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
4896 @*/
4897 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
4898 {
4899   PetscFunctionBegin;
4900   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4901   PetscValidIntPointer(depth, 3);
4902   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
4903   PetscFunctionReturn(0);
4904 }
4905 
4906 /*@
4907   DMPlexGetPointHeight - Get the height of a given point
4908 
4909   Not Collective
4910 
4911   Input Parameters:
4912 + dm    - The DMPlex object
4913 - point - The point
4914 
4915   Output Parameter:
4916 . height - The height of the point
4917 
4918   Level: intermediate
4919 
4920 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
4921 @*/
4922 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
4923 {
4924   PetscInt       n, pDepth;
4925 
4926   PetscFunctionBegin;
4927   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4928   PetscValidIntPointer(height, 3);
4929   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
4930   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
4931   *height = n - 1 - pDepth;  /* DAG depth is n-1 */
4932   PetscFunctionReturn(0);
4933 }
4934 
4935 /*@
4936   DMPlexGetCellTypeLabel - Get the DMLabel recording the polytope type of each cell
4937 
4938   Not Collective
4939 
4940   Input Parameter:
4941 . dm - The DMPlex object
4942 
4943   Output Parameter:
4944 . celltypeLabel - The DMLabel recording cell polytope type
4945 
4946   Note: This function will trigger automatica computation of cell types. This can be disabled by calling
4947   DMCreateLabel(dm, "celltype") beforehand.
4948 
4949   Level: developer
4950 
4951 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
4952 @*/
4953 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
4954 {
4955   PetscFunctionBegin;
4956   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4957   PetscValidPointer(celltypeLabel, 2);
4958   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
4959   *celltypeLabel = dm->celltypeLabel;
4960   PetscFunctionReturn(0);
4961 }
4962 
4963 /*@
4964   DMPlexGetCellType - Get the polytope type of a given cell
4965 
4966   Not Collective
4967 
4968   Input Parameters:
4969 + dm   - The DMPlex object
4970 - cell - The cell
4971 
4972   Output Parameter:
4973 . celltype - The polytope type of the cell
4974 
4975   Level: intermediate
4976 
4977 .seealso: `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
4978 @*/
4979 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
4980 {
4981   DMLabel        label;
4982   PetscInt       ct;
4983 
4984   PetscFunctionBegin;
4985   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4986   PetscValidPointer(celltype, 3);
4987   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
4988   PetscCall(DMLabelGetValue(label, cell, &ct));
4989   PetscCheck(ct >= 0,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
4990   *celltype = (DMPolytopeType) ct;
4991   PetscFunctionReturn(0);
4992 }
4993 
4994 /*@
4995   DMPlexSetCellType - Set the polytope type of a given cell
4996 
4997   Not Collective
4998 
4999   Input Parameters:
5000 + dm   - The DMPlex object
5001 . cell - The cell
5002 - celltype - The polytope type of the cell
5003 
5004   Note: By default, cell types will be automatically computed using DMPlexComputeCellTypes() before this function
5005   is executed. This function will override the computed type. However, if automatic classification will not succeed
5006   and a user wants to manually specify all types, the classification must be disabled by calling
5007   DMCreaateLabel(dm, "celltype") before getting or setting any cell types.
5008 
5009   Level: advanced
5010 
5011 .seealso: `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5012 @*/
5013 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5014 {
5015   DMLabel        label;
5016 
5017   PetscFunctionBegin;
5018   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5019   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5020   PetscCall(DMLabelSetValue(label, cell, celltype));
5021   PetscFunctionReturn(0);
5022 }
5023 
5024 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5025 {
5026   PetscSection   section, s;
5027   Mat            m;
5028   PetscInt       maxHeight;
5029 
5030   PetscFunctionBegin;
5031   PetscCall(DMClone(dm, cdm));
5032   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5033   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5034   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5035   PetscCall(DMSetLocalSection(*cdm, section));
5036   PetscCall(PetscSectionDestroy(&section));
5037   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
5038   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
5039   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
5040   PetscCall(PetscSectionDestroy(&s));
5041   PetscCall(MatDestroy(&m));
5042 
5043   PetscCall(DMSetNumFields(*cdm, 1));
5044   PetscCall(DMCreateDS(*cdm));
5045   PetscFunctionReturn(0);
5046 }
5047 
5048 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5049 {
5050   Vec coordsLocal, cellCoordsLocal;
5051   DM  coordsDM,    cellCoordsDM;
5052 
5053   PetscFunctionBegin;
5054   *field = NULL;
5055   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5056   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5057   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5058   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5059   if (coordsLocal && coordsDM) {
5060     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5061     else                                 PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5062   }
5063   PetscFunctionReturn(0);
5064 }
5065 
5066 /*@C
5067   DMPlexGetConeSection - Return a section which describes the layout of cone data
5068 
5069   Not Collective
5070 
5071   Input Parameters:
5072 . dm        - The DMPlex object
5073 
5074   Output Parameter:
5075 . section - The PetscSection object
5076 
5077   Level: developer
5078 
5079 .seealso: `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`
5080 @*/
5081 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5082 {
5083   DM_Plex *mesh = (DM_Plex*) dm->data;
5084 
5085   PetscFunctionBegin;
5086   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5087   if (section) *section = mesh->coneSection;
5088   PetscFunctionReturn(0);
5089 }
5090 
5091 /*@C
5092   DMPlexGetSupportSection - Return a section which describes the layout of support data
5093 
5094   Not Collective
5095 
5096   Input Parameters:
5097 . dm        - The DMPlex object
5098 
5099   Output Parameter:
5100 . section - The PetscSection object
5101 
5102   Level: developer
5103 
5104 .seealso: `DMPlexGetConeSection()`
5105 @*/
5106 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5107 {
5108   DM_Plex *mesh = (DM_Plex*) dm->data;
5109 
5110   PetscFunctionBegin;
5111   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5112   if (section) *section = mesh->supportSection;
5113   PetscFunctionReturn(0);
5114 }
5115 
5116 /*@C
5117   DMPlexGetCones - Return cone data
5118 
5119   Not Collective
5120 
5121   Input Parameters:
5122 . dm        - The DMPlex object
5123 
5124   Output Parameter:
5125 . cones - The cone for each point
5126 
5127   Level: developer
5128 
5129 .seealso: `DMPlexGetConeSection()`
5130 @*/
5131 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5132 {
5133   DM_Plex *mesh = (DM_Plex*) dm->data;
5134 
5135   PetscFunctionBegin;
5136   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5137   if (cones) *cones = mesh->cones;
5138   PetscFunctionReturn(0);
5139 }
5140 
5141 /*@C
5142   DMPlexGetConeOrientations - Return cone orientation data
5143 
5144   Not Collective
5145 
5146   Input Parameters:
5147 . dm        - The DMPlex object
5148 
5149   Output Parameter:
5150 . coneOrientations - The array of cone orientations for all points
5151 
5152   Level: developer
5153 
5154   Notes:
5155   The PetscSection returned by DMPlexGetConeSection() partitions coneOrientations into cone orientations of particular points as returned by DMPlexGetConeOrientation().
5156 
5157   The meaning of coneOrientations values is detailed in DMPlexGetConeOrientation().
5158 
5159 .seealso: `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`
5160 @*/
5161 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5162 {
5163   DM_Plex *mesh = (DM_Plex*) dm->data;
5164 
5165   PetscFunctionBegin;
5166   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5167   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5168   PetscFunctionReturn(0);
5169 }
5170 
5171 /******************************** FEM Support **********************************/
5172 
5173 /*
5174  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5175  representing a line in the section.
5176 */
5177 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(PetscSection section,PetscInt field,PetscInt line,PetscBool vertexchart,PetscInt *Nc,PetscInt *k)
5178 {
5179   PetscFunctionBeginHot;
5180   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5181   if (line < 0) {
5182     *k = 0;
5183     *Nc = 0;
5184   } else if (vertexchart) {            /* If we only have a vertex chart, we must have degree k=1 */
5185     *k = 1;
5186   } else {                      /* Assume the full interpolated mesh is in the chart; lines in particular */
5187     /* An order k SEM disc has k-1 dofs on an edge */
5188     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5189     *k = *k / *Nc + 1;
5190   }
5191   PetscFunctionReturn(0);
5192 }
5193 
5194 /*@
5195 
5196   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5197   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5198   section provided (or the section of the DM).
5199 
5200   Input Parameters:
5201 + dm      - The DM
5202 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or PETSC_DETERMINE
5203 - section - The PetscSection to reorder, or NULL for the default section
5204 
5205   Note: The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5206   degree of the basis.
5207 
5208   Example:
5209   A typical interpolated single-quad mesh might order points as
5210 .vb
5211   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5212 
5213   v4 -- e6 -- v3
5214   |           |
5215   e7    c0    e8
5216   |           |
5217   v1 -- e5 -- v2
5218 .ve
5219 
5220   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5221   dofs in the order of points, e.g.,
5222 .vb
5223     c0 -> [0,1,2,3]
5224     v1 -> [4]
5225     ...
5226     e5 -> [8, 9]
5227 .ve
5228 
5229   which corresponds to the dofs
5230 .vb
5231     6   10  11  7
5232     13  2   3   15
5233     12  0   1   14
5234     4   8   9   5
5235 .ve
5236 
5237   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5238 .vb
5239   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5240 .ve
5241 
5242   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5243 .vb
5244    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5245 .ve
5246 
5247   Level: developer
5248 
5249 .seealso: `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5250 @*/
5251 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5252 {
5253   DMLabel        label;
5254   PetscInt       dim, depth = -1, eStart = -1, Nf;
5255   PetscBool      vertexchart;
5256 
5257   PetscFunctionBegin;
5258   PetscCall(DMGetDimension(dm, &dim));
5259   if (dim < 1) PetscFunctionReturn(0);
5260   if (point < 0) {
5261     PetscInt sStart,sEnd;
5262 
5263     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5264     point = sEnd-sStart ? sStart : point;
5265   }
5266   PetscCall(DMPlexGetDepthLabel(dm, &label));
5267   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5268   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5269   if (depth == 1) {eStart = point;}
5270   else if  (depth == dim) {
5271     const PetscInt *cone;
5272 
5273     PetscCall(DMPlexGetCone(dm, point, &cone));
5274     if (dim == 2) eStart = cone[0];
5275     else if (dim == 3) {
5276       const PetscInt *cone2;
5277       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5278       eStart = cone2[0];
5279     } 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);
5280   } 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);
5281   {                             /* Determine whether the chart covers all points or just vertices. */
5282     PetscInt pStart,pEnd,cStart,cEnd;
5283     PetscCall(DMPlexGetDepthStratum(dm,0,&pStart,&pEnd));
5284     PetscCall(PetscSectionGetChart(section,&cStart,&cEnd));
5285     if (pStart == cStart && pEnd == cEnd) vertexchart = PETSC_TRUE;      /* Only vertices are in the chart */
5286     else if (cStart <= point && point < cEnd) vertexchart = PETSC_FALSE; /* Some interpolated points exist in the chart */
5287     else vertexchart = PETSC_TRUE;                                       /* Some interpolated points are not in chart; assume dofs only at cells and vertices */
5288   }
5289   PetscCall(PetscSectionGetNumFields(section, &Nf));
5290   for (PetscInt d=1; d<=dim; d++) {
5291     PetscInt k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5292     PetscInt *perm;
5293 
5294     for (f = 0; f < Nf; ++f) {
5295       PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5296       size += PetscPowInt(k+1, d)*Nc;
5297     }
5298     PetscCall(PetscMalloc1(size, &perm));
5299     for (f = 0; f < Nf; ++f) {
5300       switch (d) {
5301       case 1:
5302         PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5303         /*
5304          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5305          We want              [ vtx0; edge of length k-1; vtx1 ]
5306          */
5307         for (c=0; c<Nc; c++,offset++) perm[offset] = (k-1)*Nc + c + foffset;
5308         for (i=0; i<k-1; i++) for (c=0; c<Nc; c++,offset++) perm[offset] = i*Nc + c + foffset;
5309         for (c=0; c<Nc; c++,offset++) perm[offset] = k*Nc + c + foffset;
5310         foffset = offset;
5311         break;
5312       case 2:
5313         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5314         PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5315         /* The SEM order is
5316 
5317          v_lb, {e_b}, v_rb,
5318          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5319          v_lt, reverse {e_t}, v_rt
5320          */
5321         {
5322           const PetscInt of   = 0;
5323           const PetscInt oeb  = of   + PetscSqr(k-1);
5324           const PetscInt oer  = oeb  + (k-1);
5325           const PetscInt oet  = oer  + (k-1);
5326           const PetscInt oel  = oet  + (k-1);
5327           const PetscInt ovlb = oel  + (k-1);
5328           const PetscInt ovrb = ovlb + 1;
5329           const PetscInt ovrt = ovrb + 1;
5330           const PetscInt ovlt = ovrt + 1;
5331           PetscInt       o;
5332 
5333           /* bottom */
5334           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb*Nc + c + foffset;
5335           for (o = oeb; o < oer; ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5336           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb*Nc + c + foffset;
5337           /* middle */
5338           for (i = 0; i < k-1; ++i) {
5339             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel+(k-2)-i)*Nc + c + foffset;
5340             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;
5341             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer+i)*Nc + c + foffset;
5342           }
5343           /* top */
5344           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt*Nc + c + foffset;
5345           for (o = oel-1; o >= oet; --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5346           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt*Nc + c + foffset;
5347           foffset = offset;
5348         }
5349         break;
5350       case 3:
5351         /* The original hex closure is
5352 
5353          {c,
5354          f_b, f_t, f_f, f_b, f_r, f_l,
5355          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5356          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5357          */
5358         PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5359         /* The SEM order is
5360          Bottom Slice
5361          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5362          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5363          v_blb, {e_bb}, v_brb,
5364 
5365          Middle Slice (j)
5366          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5367          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5368          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5369 
5370          Top Slice
5371          v_tlf, {e_tf}, v_trf,
5372          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5373          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5374          */
5375         {
5376           const PetscInt oc    = 0;
5377           const PetscInt ofb   = oc    + PetscSqr(k-1)*(k-1);
5378           const PetscInt oft   = ofb   + PetscSqr(k-1);
5379           const PetscInt off   = oft   + PetscSqr(k-1);
5380           const PetscInt ofk   = off   + PetscSqr(k-1);
5381           const PetscInt ofr   = ofk   + PetscSqr(k-1);
5382           const PetscInt ofl   = ofr   + PetscSqr(k-1);
5383           const PetscInt oebl  = ofl   + PetscSqr(k-1);
5384           const PetscInt oebb  = oebl  + (k-1);
5385           const PetscInt oebr  = oebb  + (k-1);
5386           const PetscInt oebf  = oebr  + (k-1);
5387           const PetscInt oetf  = oebf  + (k-1);
5388           const PetscInt oetr  = oetf  + (k-1);
5389           const PetscInt oetb  = oetr  + (k-1);
5390           const PetscInt oetl  = oetb  + (k-1);
5391           const PetscInt oerf  = oetl  + (k-1);
5392           const PetscInt oelf  = oerf  + (k-1);
5393           const PetscInt oelb  = oelf  + (k-1);
5394           const PetscInt oerb  = oelb  + (k-1);
5395           const PetscInt ovblf = oerb  + (k-1);
5396           const PetscInt ovblb = ovblf + 1;
5397           const PetscInt ovbrb = ovblb + 1;
5398           const PetscInt ovbrf = ovbrb + 1;
5399           const PetscInt ovtlf = ovbrf + 1;
5400           const PetscInt ovtrf = ovtlf + 1;
5401           const PetscInt ovtrb = ovtrf + 1;
5402           const PetscInt ovtlb = ovtrb + 1;
5403           PetscInt       o, n;
5404 
5405           /* Bottom Slice */
5406           /*   bottom */
5407           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf*Nc + c + foffset;
5408           for (o = oetf-1; o >= oebf; --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5409           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf*Nc + c + foffset;
5410           /*   middle */
5411           for (i = 0; i < k-1; ++i) {
5412             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl+i)*Nc + c + foffset;
5413             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;}
5414             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr+(k-2)-i)*Nc + c + foffset;
5415           }
5416           /*   top */
5417           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb*Nc + c + foffset;
5418           for (o = oebb; o < oebr; ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5419           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb*Nc + c + foffset;
5420 
5421           /* Middle Slice */
5422           for (j = 0; j < k-1; ++j) {
5423             /*   bottom */
5424             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf+(k-2)-j)*Nc + c + foffset;
5425             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;
5426             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf+j)*Nc + c + foffset;
5427             /*   middle */
5428             for (i = 0; i < k-1; ++i) {
5429               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl+i*(k-1)+j)*Nc + c + foffset;
5430               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;
5431               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr+j*(k-1)+i)*Nc + c + foffset;
5432             }
5433             /*   top */
5434             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb+j)*Nc + c + foffset;
5435             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;
5436             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb+(k-2)-j)*Nc + c + foffset;
5437           }
5438 
5439           /* Top Slice */
5440           /*   bottom */
5441           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf*Nc + c + foffset;
5442           for (o = oetf; o < oetr; ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5443           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf*Nc + c + foffset;
5444           /*   middle */
5445           for (i = 0; i < k-1; ++i) {
5446             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl+(k-2)-i)*Nc + c + foffset;
5447             for (n = 0; n < k-1; ++n) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft+i*(k-1)+n)*Nc + c + foffset;
5448             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr+i)*Nc + c + foffset;
5449           }
5450           /*   top */
5451           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb*Nc + c + foffset;
5452           for (o = oetl-1; o >= oetb; --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5453           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb*Nc + c + foffset;
5454 
5455           foffset = offset;
5456         }
5457         break;
5458       default: SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
5459       }
5460     }
5461     PetscCheck(offset == size,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
5462     /* Check permutation */
5463     {
5464       PetscInt *check;
5465 
5466       PetscCall(PetscMalloc1(size, &check));
5467       for (i = 0; i < size; ++i) {
5468         check[i] = -1;
5469         PetscCheck(perm[i] >= 0 && perm[i] < size,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
5470       }
5471       for (i = 0; i < size; ++i) check[perm[i]] = i;
5472       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
5473       PetscCall(PetscFree(check));
5474     }
5475     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject) dm, d, size, PETSC_OWN_POINTER, perm));
5476     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
5477       PetscInt *loc_perm;
5478       PetscCall(PetscMalloc1(size*2, &loc_perm));
5479       for (PetscInt i=0; i<size; i++) {
5480         loc_perm[i] = perm[i];
5481         loc_perm[size+i] = size + perm[i];
5482       }
5483       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject) dm, d, size*2, PETSC_OWN_POINTER, loc_perm));
5484     }
5485   }
5486   PetscFunctionReturn(0);
5487 }
5488 
5489 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
5490 {
5491   PetscDS        prob;
5492   PetscInt       depth, Nf, h;
5493   DMLabel        label;
5494 
5495   PetscFunctionBeginHot;
5496   PetscCall(DMGetDS(dm, &prob));
5497   Nf      = prob->Nf;
5498   label   = dm->depthLabel;
5499   *dspace = NULL;
5500   if (field < Nf) {
5501     PetscObject disc = prob->disc[field];
5502 
5503     if (disc->classid == PETSCFE_CLASSID) {
5504       PetscDualSpace dsp;
5505 
5506       PetscCall(PetscFEGetDualSpace((PetscFE)disc,&dsp));
5507       PetscCall(DMLabelGetNumValues(label,&depth));
5508       PetscCall(DMLabelGetValue(label,point,&h));
5509       h    = depth - 1 - h;
5510       if (h) {
5511         PetscCall(PetscDualSpaceGetHeightSubspace(dsp,h,dspace));
5512       } else {
5513         *dspace = dsp;
5514       }
5515     }
5516   }
5517   PetscFunctionReturn(0);
5518 }
5519 
5520 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5521 {
5522   PetscScalar    *array, *vArray;
5523   const PetscInt *cone, *coneO;
5524   PetscInt        pStart, pEnd, p, numPoints, size = 0, offset = 0;
5525 
5526   PetscFunctionBeginHot;
5527   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5528   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
5529   PetscCall(DMPlexGetCone(dm, point, &cone));
5530   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
5531   if (!values || !*values) {
5532     if ((point >= pStart) && (point < pEnd)) {
5533       PetscInt dof;
5534 
5535       PetscCall(PetscSectionGetDof(section, point, &dof));
5536       size += dof;
5537     }
5538     for (p = 0; p < numPoints; ++p) {
5539       const PetscInt cp = cone[p];
5540       PetscInt       dof;
5541 
5542       if ((cp < pStart) || (cp >= pEnd)) continue;
5543       PetscCall(PetscSectionGetDof(section, cp, &dof));
5544       size += dof;
5545     }
5546     if (!values) {
5547       if (csize) *csize = size;
5548       PetscFunctionReturn(0);
5549     }
5550     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
5551   } else {
5552     array = *values;
5553   }
5554   size = 0;
5555   PetscCall(VecGetArray(v, &vArray));
5556   if ((point >= pStart) && (point < pEnd)) {
5557     PetscInt     dof, off, d;
5558     PetscScalar *varr;
5559 
5560     PetscCall(PetscSectionGetDof(section, point, &dof));
5561     PetscCall(PetscSectionGetOffset(section, point, &off));
5562     varr = &vArray[off];
5563     for (d = 0; d < dof; ++d, ++offset) {
5564       array[offset] = varr[d];
5565     }
5566     size += dof;
5567   }
5568   for (p = 0; p < numPoints; ++p) {
5569     const PetscInt cp = cone[p];
5570     PetscInt       o  = coneO[p];
5571     PetscInt       dof, off, d;
5572     PetscScalar   *varr;
5573 
5574     if ((cp < pStart) || (cp >= pEnd)) continue;
5575     PetscCall(PetscSectionGetDof(section, cp, &dof));
5576     PetscCall(PetscSectionGetOffset(section, cp, &off));
5577     varr = &vArray[off];
5578     if (o >= 0) {
5579       for (d = 0; d < dof; ++d, ++offset) {
5580         array[offset] = varr[d];
5581       }
5582     } else {
5583       for (d = dof-1; d >= 0; --d, ++offset) {
5584         array[offset] = varr[d];
5585       }
5586     }
5587     size += dof;
5588   }
5589   PetscCall(VecRestoreArray(v, &vArray));
5590   if (!*values) {
5591     if (csize) *csize = size;
5592     *values = array;
5593   } else {
5594     PetscCheck(size <= *csize,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5595     *csize = size;
5596   }
5597   PetscFunctionReturn(0);
5598 }
5599 
5600 /* Compress out points not in the section */
5601 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
5602 {
5603   const PetscInt np = *numPoints;
5604   PetscInt       pStart, pEnd, p, q;
5605 
5606   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5607   for (p = 0, q = 0; p < np; ++p) {
5608     const PetscInt r = points[p*2];
5609     if ((r >= pStart) && (r < pEnd)) {
5610       points[q*2]   = r;
5611       points[q*2+1] = points[p*2+1];
5612       ++q;
5613     }
5614   }
5615   *numPoints = q;
5616   return 0;
5617 }
5618 
5619 /* Compressed closure does not apply closure permutation */
5620 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5621 {
5622   const PetscInt *cla = NULL;
5623   PetscInt       np, *pts = NULL;
5624 
5625   PetscFunctionBeginHot;
5626   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject) dm, clSec, clPoints));
5627   if (*clPoints) {
5628     PetscInt dof, off;
5629 
5630     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
5631     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
5632     PetscCall(ISGetIndices(*clPoints, &cla));
5633     np   = dof/2;
5634     pts  = (PetscInt *) &cla[off];
5635   } else {
5636     PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &np, &pts));
5637     PetscCall(CompressPoints_Private(section, &np, pts));
5638   }
5639   *numPoints = np;
5640   *points    = pts;
5641   *clp       = cla;
5642   PetscFunctionReturn(0);
5643 }
5644 
5645 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5646 {
5647   PetscFunctionBeginHot;
5648   if (!*clPoints) {
5649     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
5650   } else {
5651     PetscCall(ISRestoreIndices(*clPoints, clp));
5652   }
5653   *numPoints = 0;
5654   *points    = NULL;
5655   *clSec     = NULL;
5656   *clPoints  = NULL;
5657   *clp       = NULL;
5658   PetscFunctionReturn(0);
5659 }
5660 
5661 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
5662 {
5663   PetscInt          offset = 0, p;
5664   const PetscInt    **perms = NULL;
5665   const PetscScalar **flips = NULL;
5666 
5667   PetscFunctionBeginHot;
5668   *size = 0;
5669   PetscCall(PetscSectionGetPointSyms(section,numPoints,points,&perms,&flips));
5670   for (p = 0; p < numPoints; p++) {
5671     const PetscInt    point = points[2*p];
5672     const PetscInt    *perm = perms ? perms[p] : NULL;
5673     const PetscScalar *flip = flips ? flips[p] : NULL;
5674     PetscInt          dof, off, d;
5675     const PetscScalar *varr;
5676 
5677     PetscCall(PetscSectionGetDof(section, point, &dof));
5678     PetscCall(PetscSectionGetOffset(section, point, &off));
5679     varr = &vArray[off];
5680     if (clperm) {
5681       if (perm) {
5682         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]]  = varr[d];
5683       } else {
5684         for (d = 0; d < dof; d++) array[clperm[offset +      d ]]  = varr[d];
5685       }
5686       if (flip) {
5687         for (d = 0; d < dof; d++) array[clperm[offset +      d ]] *= flip[d];
5688       }
5689     } else {
5690       if (perm) {
5691         for (d = 0; d < dof; d++) array[offset + perm[d]]  = varr[d];
5692       } else {
5693         for (d = 0; d < dof; d++) array[offset +      d ]  = varr[d];
5694       }
5695       if (flip) {
5696         for (d = 0; d < dof; d++) array[offset +      d ] *= flip[d];
5697       }
5698     }
5699     offset += dof;
5700   }
5701   PetscCall(PetscSectionRestorePointSyms(section,numPoints,points,&perms,&flips));
5702   *size = offset;
5703   PetscFunctionReturn(0);
5704 }
5705 
5706 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[])
5707 {
5708   PetscInt          offset = 0, f;
5709 
5710   PetscFunctionBeginHot;
5711   *size = 0;
5712   for (f = 0; f < numFields; ++f) {
5713     PetscInt          p;
5714     const PetscInt    **perms = NULL;
5715     const PetscScalar **flips = NULL;
5716 
5717     PetscCall(PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips));
5718     for (p = 0; p < numPoints; p++) {
5719       const PetscInt    point = points[2*p];
5720       PetscInt          fdof, foff, b;
5721       const PetscScalar *varr;
5722       const PetscInt    *perm = perms ? perms[p] : NULL;
5723       const PetscScalar *flip = flips ? flips[p] : NULL;
5724 
5725       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
5726       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
5727       varr = &vArray[foff];
5728       if (clperm) {
5729         if (perm) {for (b = 0; b < fdof; b++) {array[clperm[offset + perm[b]]]  = varr[b];}}
5730         else      {for (b = 0; b < fdof; b++) {array[clperm[offset +      b ]]  = varr[b];}}
5731         if (flip) {for (b = 0; b < fdof; b++) {array[clperm[offset +      b ]] *= flip[b];}}
5732       } else {
5733         if (perm) {for (b = 0; b < fdof; b++) {array[offset + perm[b]]  = varr[b];}}
5734         else      {for (b = 0; b < fdof; b++) {array[offset +      b ]  = varr[b];}}
5735         if (flip) {for (b = 0; b < fdof; b++) {array[offset +      b ] *= flip[b];}}
5736       }
5737       offset += fdof;
5738     }
5739     PetscCall(PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips));
5740   }
5741   *size = offset;
5742   PetscFunctionReturn(0);
5743 }
5744 
5745 /*@C
5746   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
5747 
5748   Not collective
5749 
5750   Input Parameters:
5751 + dm - The DM
5752 . section - The section describing the layout in v, or NULL to use the default section
5753 . v - The local vector
5754 - point - The point in the DM
5755 
5756   Input/Output Parameters:
5757 + csize  - The size of the input values array, or NULL; on output the number of values in the closure
5758 - values - An array to use for the values, or NULL to have it allocated automatically;
5759            if the user provided NULL, it is a borrowed array and should not be freed
5760 
5761 $ Note that DMPlexVecGetClosure/DMPlexVecRestoreClosure only allocates the values array if it set to NULL in the
5762 $ calling function. This is because DMPlexVecGetClosure() is typically called in the inner loop of a Vec or Mat
5763 $ assembly function, and a user may already have allocated storage for this operation.
5764 $
5765 $ A typical use could be
5766 $
5767 $  values = NULL;
5768 $  PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5769 $  for (cl = 0; cl < clSize; ++cl) {
5770 $    <Compute on closure>
5771 $  }
5772 $  PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
5773 $
5774 $ or
5775 $
5776 $  PetscMalloc1(clMaxSize, &values);
5777 $  for (p = pStart; p < pEnd; ++p) {
5778 $    clSize = clMaxSize;
5779 $    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5780 $    for (cl = 0; cl < clSize; ++cl) {
5781 $      <Compute on closure>
5782 $    }
5783 $  }
5784 $  PetscFree(values);
5785 
5786   Fortran Notes:
5787   Since it returns an array, this routine is only available in Fortran 90, and you must
5788   include petsc.h90 in your code.
5789 
5790   The csize argument is not present in the Fortran 90 binding since it is internal to the array.
5791 
5792   Level: intermediate
5793 
5794 .seealso `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
5795 @*/
5796 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5797 {
5798   PetscSection       clSection;
5799   IS                 clPoints;
5800   PetscInt          *points = NULL;
5801   const PetscInt    *clp, *perm;
5802   PetscInt           depth, numFields, numPoints, asize;
5803 
5804   PetscFunctionBeginHot;
5805   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5806   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5807   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5808   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5809   PetscCall(DMPlexGetDepth(dm, &depth));
5810   PetscCall(PetscSectionGetNumFields(section, &numFields));
5811   if (depth == 1 && numFields < 2) {
5812     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
5813     PetscFunctionReturn(0);
5814   }
5815   /* Get points */
5816   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5817   /* Get sizes */
5818   asize = 0;
5819   for (PetscInt p = 0; p < numPoints*2; p += 2) {
5820     PetscInt dof;
5821     PetscCall(PetscSectionGetDof(section, points[p], &dof));
5822     asize += dof;
5823   }
5824   if (values) {
5825     const PetscScalar *vArray;
5826     PetscInt          size;
5827 
5828     if (*values) {
5829       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);
5830     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
5831     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, asize, &perm));
5832     PetscCall(VecGetArrayRead(v, &vArray));
5833     /* Get values */
5834     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
5835     else               PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
5836     PetscCheck(asize == size,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
5837     /* Cleanup array */
5838     PetscCall(VecRestoreArrayRead(v, &vArray));
5839   }
5840   if (csize) *csize = asize;
5841   /* Cleanup points */
5842   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5843   PetscFunctionReturn(0);
5844 }
5845 
5846 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
5847 {
5848   DMLabel            depthLabel;
5849   PetscSection       clSection;
5850   IS                 clPoints;
5851   PetscScalar       *array;
5852   const PetscScalar *vArray;
5853   PetscInt          *points = NULL;
5854   const PetscInt    *clp, *perm = NULL;
5855   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
5856 
5857   PetscFunctionBeginHot;
5858   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5859   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5860   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5861   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5862   PetscCall(DMPlexGetDepth(dm, &mdepth));
5863   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5864   PetscCall(PetscSectionGetNumFields(section, &numFields));
5865   if (mdepth == 1 && numFields < 2) {
5866     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
5867     PetscFunctionReturn(0);
5868   }
5869   /* Get points */
5870   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5871   for (clsize=0,p=0; p<Np; p++) {
5872     PetscInt dof;
5873     PetscCall(PetscSectionGetDof(section, points[2*p], &dof));
5874     clsize += dof;
5875   }
5876   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, clsize, &perm));
5877   /* Filter points */
5878   for (p = 0; p < numPoints*2; p += 2) {
5879     PetscInt dep;
5880 
5881     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
5882     if (dep != depth) continue;
5883     points[Np*2+0] = points[p];
5884     points[Np*2+1] = points[p+1];
5885     ++Np;
5886   }
5887   /* Get array */
5888   if (!values || !*values) {
5889     PetscInt asize = 0, dof;
5890 
5891     for (p = 0; p < Np*2; p += 2) {
5892       PetscCall(PetscSectionGetDof(section, points[p], &dof));
5893       asize += dof;
5894     }
5895     if (!values) {
5896       PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5897       if (csize) *csize = asize;
5898       PetscFunctionReturn(0);
5899     }
5900     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
5901   } else {
5902     array = *values;
5903   }
5904   PetscCall(VecGetArrayRead(v, &vArray));
5905   /* Get values */
5906   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
5907   else               PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
5908   /* Cleanup points */
5909   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5910   /* Cleanup array */
5911   PetscCall(VecRestoreArrayRead(v, &vArray));
5912   if (!*values) {
5913     if (csize) *csize = size;
5914     *values = array;
5915   } else {
5916     PetscCheck(size <= *csize,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5917     *csize = size;
5918   }
5919   PetscFunctionReturn(0);
5920 }
5921 
5922 /*@C
5923   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
5924 
5925   Not collective
5926 
5927   Input Parameters:
5928 + dm - The DM
5929 . section - The section describing the layout in v, or NULL to use the default section
5930 . v - The local vector
5931 . point - The point in the DM
5932 . csize - The number of values in the closure, or NULL
5933 - values - The array of values, which is a borrowed array and should not be freed
5934 
5935   Note that the array values are discarded and not copied back into v. In order to copy values back to v, use DMPlexVecSetClosure()
5936 
5937   Fortran Notes:
5938   Since it returns an array, this routine is only available in Fortran 90, and you must
5939   include petsc.h90 in your code.
5940 
5941   The csize argument is not present in the Fortran 90 binding since it is internal to the array.
5942 
5943   Level: intermediate
5944 
5945 .seealso `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
5946 @*/
5947 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5948 {
5949   PetscInt       size = 0;
5950 
5951   PetscFunctionBegin;
5952   /* Should work without recalculating size */
5953   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void*) values));
5954   *values = NULL;
5955   PetscFunctionReturn(0);
5956 }
5957 
5958 static inline void add   (PetscScalar *x, PetscScalar y) {*x += y;}
5959 static inline void insert(PetscScalar *x, PetscScalar y) {*x  = y;}
5960 
5961 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[])
5962 {
5963   PetscInt        cdof;   /* The number of constraints on this point */
5964   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
5965   PetscScalar    *a;
5966   PetscInt        off, cind = 0, k;
5967 
5968   PetscFunctionBegin;
5969   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
5970   PetscCall(PetscSectionGetOffset(section, point, &off));
5971   a    = &array[off];
5972   if (!cdof || setBC) {
5973     if (clperm) {
5974       if (perm) {for (k = 0; k < dof; ++k) {fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));}}
5975       else      {for (k = 0; k < dof; ++k) {fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));}}
5976     } else {
5977       if (perm) {for (k = 0; k < dof; ++k) {fuse(&a[k], values[offset+perm[k]] * (flip ? flip[perm[k]] : 1.));}}
5978       else      {for (k = 0; k < dof; ++k) {fuse(&a[k], values[offset+     k ] * (flip ? flip[     k ] : 1.));}}
5979     }
5980   } else {
5981     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
5982     if (clperm) {
5983       if (perm) {for (k = 0; k < dof; ++k) {
5984           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5985           fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));
5986         }
5987       } else {
5988         for (k = 0; k < dof; ++k) {
5989           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5990           fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));
5991         }
5992       }
5993     } else {
5994       if (perm) {
5995         for (k = 0; k < dof; ++k) {
5996           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5997           fuse(&a[k], values[offset+perm[k]] * (flip ? flip[perm[k]] : 1.));
5998         }
5999       } else {
6000         for (k = 0; k < dof; ++k) {
6001           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
6002           fuse(&a[k], values[offset+     k ] * (flip ? flip[     k ] : 1.));
6003         }
6004       }
6005     }
6006   }
6007   PetscFunctionReturn(0);
6008 }
6009 
6010 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[])
6011 {
6012   PetscInt        cdof;   /* The number of constraints on this point */
6013   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6014   PetscScalar    *a;
6015   PetscInt        off, cind = 0, k;
6016 
6017   PetscFunctionBegin;
6018   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6019   PetscCall(PetscSectionGetOffset(section, point, &off));
6020   a    = &array[off];
6021   if (cdof) {
6022     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6023     if (clperm) {
6024       if (perm) {
6025         for (k = 0; k < dof; ++k) {
6026           if ((cind < cdof) && (k == cdofs[cind])) {
6027             fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));
6028             cind++;
6029           }
6030         }
6031       } else {
6032         for (k = 0; k < dof; ++k) {
6033           if ((cind < cdof) && (k == cdofs[cind])) {
6034             fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));
6035             cind++;
6036           }
6037         }
6038       }
6039     } else {
6040       if (perm) {
6041         for (k = 0; k < dof; ++k) {
6042           if ((cind < cdof) && (k == cdofs[cind])) {
6043             fuse(&a[k], values[offset+perm[k]] * (flip ? flip[perm[k]] : 1.));
6044             cind++;
6045           }
6046         }
6047       } else {
6048         for (k = 0; k < dof; ++k) {
6049           if ((cind < cdof) && (k == cdofs[cind])) {
6050             fuse(&a[k], values[offset+     k ] * (flip ? flip[     k ] : 1.));
6051             cind++;
6052           }
6053         }
6054       }
6055     }
6056   }
6057   PetscFunctionReturn(0);
6058 }
6059 
6060 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[])
6061 {
6062   PetscScalar    *a;
6063   PetscInt        fdof, foff, fcdof, foffset = *offset;
6064   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6065   PetscInt        cind = 0, b;
6066 
6067   PetscFunctionBegin;
6068   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6069   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6070   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6071   a    = &array[foff];
6072   if (!fcdof || setBC) {
6073     if (clperm) {
6074       if (perm) {for (b = 0; b < fdof; b++) {fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));}}
6075       else      {for (b = 0; b < fdof; b++) {fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));}}
6076     } else {
6077       if (perm) {for (b = 0; b < fdof; b++) {fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));}}
6078       else      {for (b = 0; b < fdof; b++) {fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));}}
6079     }
6080   } else {
6081     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6082     if (clperm) {
6083       if (perm) {
6084         for (b = 0; b < fdof; b++) {
6085           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6086           fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));
6087         }
6088       } else {
6089         for (b = 0; b < fdof; b++) {
6090           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6091           fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));
6092         }
6093       }
6094     } else {
6095       if (perm) {
6096         for (b = 0; b < fdof; b++) {
6097           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6098           fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));
6099         }
6100       } else {
6101         for (b = 0; b < fdof; b++) {
6102           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6103           fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));
6104         }
6105       }
6106     }
6107   }
6108   *offset += fdof;
6109   PetscFunctionReturn(0);
6110 }
6111 
6112 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[])
6113 {
6114   PetscScalar    *a;
6115   PetscInt        fdof, foff, fcdof, foffset = *offset;
6116   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6117   PetscInt        Nc, cind = 0, ncind = 0, b;
6118   PetscBool       ncSet, fcSet;
6119 
6120   PetscFunctionBegin;
6121   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6122   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6123   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6124   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6125   a    = &array[foff];
6126   if (fcdof) {
6127     /* We just override fcdof and fcdofs with Ncc and comps */
6128     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6129     if (clperm) {
6130       if (perm) {
6131         if (comps) {
6132           for (b = 0; b < fdof; b++) {
6133             ncSet = fcSet = PETSC_FALSE;
6134             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6135             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6136             if (ncSet && fcSet) {fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));}
6137           }
6138         } else {
6139           for (b = 0; b < fdof; b++) {
6140             if ((cind < fcdof) && (b == fcdofs[cind])) {
6141               fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));
6142               ++cind;
6143             }
6144           }
6145         }
6146       } else {
6147         if (comps) {
6148           for (b = 0; b < fdof; b++) {
6149             ncSet = fcSet = PETSC_FALSE;
6150             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6151             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6152             if (ncSet && fcSet) {fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));}
6153           }
6154         } else {
6155           for (b = 0; b < fdof; b++) {
6156             if ((cind < fcdof) && (b == fcdofs[cind])) {
6157               fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));
6158               ++cind;
6159             }
6160           }
6161         }
6162       }
6163     } else {
6164       if (perm) {
6165         if (comps) {
6166           for (b = 0; b < fdof; b++) {
6167             ncSet = fcSet = PETSC_FALSE;
6168             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6169             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6170             if (ncSet && fcSet) {fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));}
6171           }
6172         } else {
6173           for (b = 0; b < fdof; b++) {
6174             if ((cind < fcdof) && (b == fcdofs[cind])) {
6175               fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));
6176               ++cind;
6177             }
6178           }
6179         }
6180       } else {
6181         if (comps) {
6182           for (b = 0; b < fdof; b++) {
6183             ncSet = fcSet = PETSC_FALSE;
6184             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6185             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6186             if (ncSet && fcSet) {fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));}
6187           }
6188         } else {
6189           for (b = 0; b < fdof; b++) {
6190             if ((cind < fcdof) && (b == fcdofs[cind])) {
6191               fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));
6192               ++cind;
6193             }
6194           }
6195         }
6196       }
6197     }
6198   }
6199   *offset += fdof;
6200   PetscFunctionReturn(0);
6201 }
6202 
6203 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6204 {
6205   PetscScalar    *array;
6206   const PetscInt *cone, *coneO;
6207   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6208 
6209   PetscFunctionBeginHot;
6210   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6211   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6212   PetscCall(DMPlexGetCone(dm, point, &cone));
6213   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6214   PetscCall(VecGetArray(v, &array));
6215   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6216     const PetscInt cp = !p ? point : cone[p-1];
6217     const PetscInt o  = !p ? 0     : coneO[p-1];
6218 
6219     if ((cp < pStart) || (cp >= pEnd)) {dof = 0; continue;}
6220     PetscCall(PetscSectionGetDof(section, cp, &dof));
6221     /* ADD_VALUES */
6222     {
6223       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6224       PetscScalar    *a;
6225       PetscInt        cdof, coff, cind = 0, k;
6226 
6227       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6228       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6229       a    = &array[coff];
6230       if (!cdof) {
6231         if (o >= 0) {
6232           for (k = 0; k < dof; ++k) {
6233             a[k] += values[off+k];
6234           }
6235         } else {
6236           for (k = 0; k < dof; ++k) {
6237             a[k] += values[off+dof-k-1];
6238           }
6239         }
6240       } else {
6241         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6242         if (o >= 0) {
6243           for (k = 0; k < dof; ++k) {
6244             if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
6245             a[k] += values[off+k];
6246           }
6247         } else {
6248           for (k = 0; k < dof; ++k) {
6249             if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
6250             a[k] += values[off+dof-k-1];
6251           }
6252         }
6253       }
6254     }
6255   }
6256   PetscCall(VecRestoreArray(v, &array));
6257   PetscFunctionReturn(0);
6258 }
6259 
6260 /*@C
6261   DMPlexVecSetClosure - Set an array of the values on the closure of 'point'
6262 
6263   Not collective
6264 
6265   Input Parameters:
6266 + dm - The DM
6267 . section - The section describing the layout in v, or NULL to use the default section
6268 . v - The local vector
6269 . point - The point in the DM
6270 . values - The array of values
6271 - mode - The insert mode. One of INSERT_ALL_VALUES, ADD_ALL_VALUES, INSERT_VALUES, ADD_VALUES, INSERT_BC_VALUES, and ADD_BC_VALUES,
6272          where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions.
6273 
6274   Fortran Notes:
6275   This routine is only available in Fortran 90, and you must include petsc.h90 in your code.
6276 
6277   Level: intermediate
6278 
6279 .seealso `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6280 @*/
6281 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6282 {
6283   PetscSection    clSection;
6284   IS              clPoints;
6285   PetscScalar    *array;
6286   PetscInt       *points = NULL;
6287   const PetscInt *clp, *clperm = NULL;
6288   PetscInt        depth, numFields, numPoints, p, clsize;
6289 
6290   PetscFunctionBeginHot;
6291   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6292   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6293   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6294   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6295   PetscCall(DMPlexGetDepth(dm, &depth));
6296   PetscCall(PetscSectionGetNumFields(section, &numFields));
6297   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6298     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6299     PetscFunctionReturn(0);
6300   }
6301   /* Get points */
6302   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6303   for (clsize=0,p=0; p<numPoints; p++) {
6304     PetscInt dof;
6305     PetscCall(PetscSectionGetDof(section, points[2*p], &dof));
6306     clsize += dof;
6307   }
6308   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, clsize, &clperm));
6309   /* Get array */
6310   PetscCall(VecGetArray(v, &array));
6311   /* Get values */
6312   if (numFields > 0) {
6313     PetscInt offset = 0, f;
6314     for (f = 0; f < numFields; ++f) {
6315       const PetscInt    **perms = NULL;
6316       const PetscScalar **flips = NULL;
6317 
6318       PetscCall(PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6319       switch (mode) {
6320       case INSERT_VALUES:
6321         for (p = 0; p < numPoints; p++) {
6322           const PetscInt    point = points[2*p];
6323           const PetscInt    *perm = perms ? perms[p] : NULL;
6324           const PetscScalar *flip = flips ? flips[p] : NULL;
6325           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array);
6326         } break;
6327       case INSERT_ALL_VALUES:
6328         for (p = 0; p < numPoints; p++) {
6329           const PetscInt    point = points[2*p];
6330           const PetscInt    *perm = perms ? perms[p] : NULL;
6331           const PetscScalar *flip = flips ? flips[p] : NULL;
6332           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array);
6333         } break;
6334       case INSERT_BC_VALUES:
6335         for (p = 0; p < numPoints; p++) {
6336           const PetscInt    point = points[2*p];
6337           const PetscInt    *perm = perms ? perms[p] : NULL;
6338           const PetscScalar *flip = flips ? flips[p] : NULL;
6339           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array);
6340         } break;
6341       case ADD_VALUES:
6342         for (p = 0; p < numPoints; p++) {
6343           const PetscInt    point = points[2*p];
6344           const PetscInt    *perm = perms ? perms[p] : NULL;
6345           const PetscScalar *flip = flips ? flips[p] : NULL;
6346           updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array);
6347         } break;
6348       case ADD_ALL_VALUES:
6349         for (p = 0; p < numPoints; p++) {
6350           const PetscInt    point = points[2*p];
6351           const PetscInt    *perm = perms ? perms[p] : NULL;
6352           const PetscScalar *flip = flips ? flips[p] : NULL;
6353           updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array);
6354         } break;
6355       case ADD_BC_VALUES:
6356         for (p = 0; p < numPoints; p++) {
6357           const PetscInt    point = points[2*p];
6358           const PetscInt    *perm = perms ? perms[p] : NULL;
6359           const PetscScalar *flip = flips ? flips[p] : NULL;
6360           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array);
6361         } break;
6362       default:
6363         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6364       }
6365       PetscCall(PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6366     }
6367   } else {
6368     PetscInt dof, off;
6369     const PetscInt    **perms = NULL;
6370     const PetscScalar **flips = NULL;
6371 
6372     PetscCall(PetscSectionGetPointSyms(section,numPoints,points,&perms,&flips));
6373     switch (mode) {
6374     case INSERT_VALUES:
6375       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6376         const PetscInt    point = points[2*p];
6377         const PetscInt    *perm = perms ? perms[p] : NULL;
6378         const PetscScalar *flip = flips ? flips[p] : NULL;
6379         PetscCall(PetscSectionGetDof(section, point, &dof));
6380         updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array);
6381       } break;
6382     case INSERT_ALL_VALUES:
6383       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6384         const PetscInt    point = points[2*p];
6385         const PetscInt    *perm = perms ? perms[p] : NULL;
6386         const PetscScalar *flip = flips ? flips[p] : NULL;
6387         PetscCall(PetscSectionGetDof(section, point, &dof));
6388         updatePoint_private(section, point, dof, insert, PETSC_TRUE,  perm, flip, clperm, values, off, array);
6389       } break;
6390     case INSERT_BC_VALUES:
6391       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6392         const PetscInt    point = points[2*p];
6393         const PetscInt    *perm = perms ? perms[p] : NULL;
6394         const PetscScalar *flip = flips ? flips[p] : NULL;
6395         PetscCall(PetscSectionGetDof(section, point, &dof));
6396         updatePointBC_private(section, point, dof, insert,  perm, flip, clperm, values, off, array);
6397       } break;
6398     case ADD_VALUES:
6399       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6400         const PetscInt    point = points[2*p];
6401         const PetscInt    *perm = perms ? perms[p] : NULL;
6402         const PetscScalar *flip = flips ? flips[p] : NULL;
6403         PetscCall(PetscSectionGetDof(section, point, &dof));
6404         updatePoint_private(section, point, dof, add,    PETSC_FALSE, perm, flip, clperm, values, off, array);
6405       } break;
6406     case ADD_ALL_VALUES:
6407       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6408         const PetscInt    point = points[2*p];
6409         const PetscInt    *perm = perms ? perms[p] : NULL;
6410         const PetscScalar *flip = flips ? flips[p] : NULL;
6411         PetscCall(PetscSectionGetDof(section, point, &dof));
6412         updatePoint_private(section, point, dof, add,    PETSC_TRUE,  perm, flip, clperm, values, off, array);
6413       } break;
6414     case ADD_BC_VALUES:
6415       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6416         const PetscInt    point = points[2*p];
6417         const PetscInt    *perm = perms ? perms[p] : NULL;
6418         const PetscScalar *flip = flips ? flips[p] : NULL;
6419         PetscCall(PetscSectionGetDof(section, point, &dof));
6420         updatePointBC_private(section, point, dof, add,  perm, flip, clperm, values, off, array);
6421       } break;
6422     default:
6423       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6424     }
6425     PetscCall(PetscSectionRestorePointSyms(section,numPoints,points,&perms,&flips));
6426   }
6427   /* Cleanup points */
6428   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6429   /* Cleanup array */
6430   PetscCall(VecRestoreArray(v, &array));
6431   PetscFunctionReturn(0);
6432 }
6433 
6434 /* Check whether the given point is in the label. If not, update the offset to skip this point */
6435 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset)
6436 {
6437   PetscFunctionBegin;
6438   if (label) {
6439     PetscInt       val, fdof;
6440 
6441     /* There is a problem with this:
6442          Suppose we have two label values, defining surfaces, interecting along a line in 3D. When we add cells to the label, the cells that
6443        touch both surfaces must pick a label value. Thus we miss setting values for the surface with that other value intersecting that cell.
6444        Thus I am only going to check val != -1, not val != labelId
6445     */
6446     PetscCall(DMLabelGetValue(label, point, &val));
6447     if (val < 0) {
6448       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6449       *offset += fdof;
6450       PetscFunctionReturn(1);
6451     }
6452   }
6453   PetscFunctionReturn(0);
6454 }
6455 
6456 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
6457 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)
6458 {
6459   PetscSection    clSection;
6460   IS              clPoints;
6461   PetscScalar    *array;
6462   PetscInt       *points = NULL;
6463   const PetscInt *clp;
6464   PetscInt        numFields, numPoints, p;
6465   PetscInt        offset = 0, f;
6466 
6467   PetscFunctionBeginHot;
6468   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6469   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6470   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6471   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6472   PetscCall(PetscSectionGetNumFields(section, &numFields));
6473   /* Get points */
6474   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6475   /* Get array */
6476   PetscCall(VecGetArray(v, &array));
6477   /* Get values */
6478   for (f = 0; f < numFields; ++f) {
6479     const PetscInt    **perms = NULL;
6480     const PetscScalar **flips = NULL;
6481 
6482     if (!fieldActive[f]) {
6483       for (p = 0; p < numPoints*2; p += 2) {
6484         PetscInt fdof;
6485         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
6486         offset += fdof;
6487       }
6488       continue;
6489     }
6490     PetscCall(PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6491     switch (mode) {
6492     case INSERT_VALUES:
6493       for (p = 0; p < numPoints; p++) {
6494         const PetscInt    point = points[2*p];
6495         const PetscInt    *perm = perms ? perms[p] : NULL;
6496         const PetscScalar *flip = flips ? flips[p] : NULL;
6497         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6498         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
6499       } break;
6500     case INSERT_ALL_VALUES:
6501       for (p = 0; p < numPoints; p++) {
6502         const PetscInt    point = points[2*p];
6503         const PetscInt    *perm = perms ? perms[p] : NULL;
6504         const PetscScalar *flip = flips ? flips[p] : NULL;
6505         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6506         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
6507       } break;
6508     case INSERT_BC_VALUES:
6509       for (p = 0; p < numPoints; p++) {
6510         const PetscInt    point = points[2*p];
6511         const PetscInt    *perm = perms ? perms[p] : NULL;
6512         const PetscScalar *flip = flips ? flips[p] : NULL;
6513         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6514         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
6515       } break;
6516     case ADD_VALUES:
6517       for (p = 0; p < numPoints; p++) {
6518         const PetscInt    point = points[2*p];
6519         const PetscInt    *perm = perms ? perms[p] : NULL;
6520         const PetscScalar *flip = flips ? flips[p] : NULL;
6521         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6522         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
6523       } break;
6524     case ADD_ALL_VALUES:
6525       for (p = 0; p < numPoints; p++) {
6526         const PetscInt    point = points[2*p];
6527         const PetscInt    *perm = perms ? perms[p] : NULL;
6528         const PetscScalar *flip = flips ? flips[p] : NULL;
6529         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6530         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
6531       } break;
6532     default:
6533       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6534     }
6535     PetscCall(PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6536   }
6537   /* Cleanup points */
6538   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6539   /* Cleanup array */
6540   PetscCall(VecRestoreArray(v, &array));
6541   PetscFunctionReturn(0);
6542 }
6543 
6544 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
6545 {
6546   PetscMPIInt    rank;
6547   PetscInt       i, j;
6548 
6549   PetscFunctionBegin;
6550   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
6551   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
6552   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
6553   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
6554   numCIndices = numCIndices ? numCIndices : numRIndices;
6555   if (!values) PetscFunctionReturn(0);
6556   for (i = 0; i < numRIndices; i++) {
6557     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
6558     for (j = 0; j < numCIndices; j++) {
6559 #if defined(PETSC_USE_COMPLEX)
6560       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i*numCIndices+j]), (double)PetscImaginaryPart(values[i*numCIndices+j])));
6561 #else
6562       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i*numCIndices+j]));
6563 #endif
6564     }
6565     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
6566   }
6567   PetscFunctionReturn(0);
6568 }
6569 
6570 /*
6571   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
6572 
6573   Input Parameters:
6574 + section - The section for this data layout
6575 . islocal - Is the section (and thus indices being requested) local or global?
6576 . point   - The point contributing dofs with these indices
6577 . off     - The global offset of this point
6578 . loff    - The local offset of each field
6579 . setBC   - The flag determining whether to include indices of boundary values
6580 . perm    - A permutation of the dofs on this point, or NULL
6581 - indperm - A permutation of the entire indices array, or NULL
6582 
6583   Output Parameter:
6584 . indices - Indices for dofs on this point
6585 
6586   Level: developer
6587 
6588   Note: The indices could be local or global, depending on the value of 'off'.
6589 */
6590 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal,PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
6591 {
6592   PetscInt        dof;   /* The number of unknowns on this point */
6593   PetscInt        cdof;  /* The number of constraints on this point */
6594   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6595   PetscInt        cind = 0, k;
6596 
6597   PetscFunctionBegin;
6598   PetscCheck(islocal || !setBC,PetscObjectComm((PetscObject)section),PETSC_ERR_ARG_INCOMP,"setBC incompatible with global indices; use a local section or disable setBC");
6599   PetscCall(PetscSectionGetDof(section, point, &dof));
6600   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6601   if (!cdof || setBC) {
6602     for (k = 0; k < dof; ++k) {
6603       const PetscInt preind = perm ? *loff+perm[k] : *loff+k;
6604       const PetscInt ind    = indperm ? indperm[preind] : preind;
6605 
6606       indices[ind] = off + k;
6607     }
6608   } else {
6609     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6610     for (k = 0; k < dof; ++k) {
6611       const PetscInt preind = perm ? *loff+perm[k] : *loff+k;
6612       const PetscInt ind    = indperm ? indperm[preind] : preind;
6613 
6614       if ((cind < cdof) && (k == cdofs[cind])) {
6615         /* Insert check for returning constrained indices */
6616         indices[ind] = -(off+k+1);
6617         ++cind;
6618       } else {
6619         indices[ind] = off + k - (islocal ? 0 : cind);
6620       }
6621     }
6622   }
6623   *loff += dof;
6624   PetscFunctionReturn(0);
6625 }
6626 
6627 /*
6628  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
6629 
6630  Input Parameters:
6631 + section - a section (global or local)
6632 - islocal - PETSC_TRUE if requesting local indices (i.e., section is local); PETSC_FALSE for global
6633 . point - point within section
6634 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
6635 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
6636 . setBC - identify constrained (boundary condition) points via involution.
6637 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
6638 . permsoff - offset
6639 - indperm - index permutation
6640 
6641  Output Parameter:
6642 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
6643 . indices - array to hold indices (as defined by section) of each dof associated with point
6644 
6645  Notes:
6646  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
6647  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
6648  in the local vector.
6649 
6650  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
6651  significant).  It is invalid to call with a global section and setBC=true.
6652 
6653  Developer Note:
6654  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
6655  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
6656  offset could be obtained from the section instead of passing it explicitly as we do now.
6657 
6658  Example:
6659  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
6660  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
6661  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
6662  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.
6663 
6664  Level: developer
6665 */
6666 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[])
6667 {
6668   PetscInt       numFields, foff, f;
6669 
6670   PetscFunctionBegin;
6671   PetscCheck(islocal || !setBC,PetscObjectComm((PetscObject)section),PETSC_ERR_ARG_INCOMP,"setBC incompatible with global indices; use a local section or disable setBC");
6672   PetscCall(PetscSectionGetNumFields(section, &numFields));
6673   for (f = 0, foff = 0; f < numFields; ++f) {
6674     PetscInt        fdof, cfdof;
6675     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6676     PetscInt        cind = 0, b;
6677     const PetscInt  *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6678 
6679     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6680     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
6681     if (!cfdof || setBC) {
6682       for (b = 0; b < fdof; ++b) {
6683         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6684         const PetscInt ind    = indperm ? indperm[preind] : preind;
6685 
6686         indices[ind] = off+foff+b;
6687       }
6688     } else {
6689       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6690       for (b = 0; b < fdof; ++b) {
6691         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6692         const PetscInt ind    = indperm ? indperm[preind] : preind;
6693 
6694         if ((cind < cfdof) && (b == fcdofs[cind])) {
6695           indices[ind] = -(off+foff+b+1);
6696           ++cind;
6697         } else {
6698           indices[ind] = off + foff + b - (islocal ? 0 : cind);
6699         }
6700       }
6701     }
6702     foff     += (setBC || islocal ? fdof : (fdof - cfdof));
6703     foffs[f] += fdof;
6704   }
6705   PetscFunctionReturn(0);
6706 }
6707 
6708 /*
6709   This version believes the globalSection offsets for each field, rather than just the point offset
6710 
6711  . foffs - The offset into 'indices' for each field, since it is segregated by field
6712 
6713  Notes:
6714  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
6715  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
6716 */
6717 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
6718 {
6719   PetscInt       numFields, foff, f;
6720 
6721   PetscFunctionBegin;
6722   PetscCall(PetscSectionGetNumFields(section, &numFields));
6723   for (f = 0; f < numFields; ++f) {
6724     PetscInt        fdof, cfdof;
6725     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6726     PetscInt        cind = 0, b;
6727     const PetscInt  *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6728 
6729     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6730     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
6731     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
6732     if (!cfdof) {
6733       for (b = 0; b < fdof; ++b) {
6734         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6735         const PetscInt ind    = indperm ? indperm[preind] : preind;
6736 
6737         indices[ind] = foff+b;
6738       }
6739     } else {
6740       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6741       for (b = 0; b < fdof; ++b) {
6742         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6743         const PetscInt ind    = indperm ? indperm[preind] : preind;
6744 
6745         if ((cind < cfdof) && (b == fcdofs[cind])) {
6746           indices[ind] = -(foff+b+1);
6747           ++cind;
6748         } else {
6749           indices[ind] = foff+b-cind;
6750         }
6751       }
6752     }
6753     foffs[f] += fdof;
6754   }
6755   PetscFunctionReturn(0);
6756 }
6757 
6758 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)
6759 {
6760   Mat             cMat;
6761   PetscSection    aSec, cSec;
6762   IS              aIS;
6763   PetscInt        aStart = -1, aEnd = -1;
6764   const PetscInt  *anchors;
6765   PetscInt        numFields, f, p, q, newP = 0;
6766   PetscInt        newNumPoints = 0, newNumIndices = 0;
6767   PetscInt        *newPoints, *indices, *newIndices;
6768   PetscInt        maxAnchor, maxDof;
6769   PetscInt        newOffsets[32];
6770   PetscInt        *pointMatOffsets[32];
6771   PetscInt        *newPointOffsets[32];
6772   PetscScalar     *pointMat[32];
6773   PetscScalar     *newValues=NULL,*tmpValues;
6774   PetscBool       anyConstrained = PETSC_FALSE;
6775 
6776   PetscFunctionBegin;
6777   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6778   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6779   PetscCall(PetscSectionGetNumFields(section, &numFields));
6780 
6781   PetscCall(DMPlexGetAnchors(dm,&aSec,&aIS));
6782   /* if there are point-to-point constraints */
6783   if (aSec) {
6784     PetscCall(PetscArrayzero(newOffsets, 32));
6785     PetscCall(ISGetIndices(aIS,&anchors));
6786     PetscCall(PetscSectionGetChart(aSec,&aStart,&aEnd));
6787     /* figure out how many points are going to be in the new element matrix
6788      * (we allow double counting, because it's all just going to be summed
6789      * into the global matrix anyway) */
6790     for (p = 0; p < 2*numPoints; p+=2) {
6791       PetscInt b    = points[p];
6792       PetscInt bDof = 0, bSecDof;
6793 
6794       PetscCall(PetscSectionGetDof(section,b,&bSecDof));
6795       if (!bSecDof) {
6796         continue;
6797       }
6798       if (b >= aStart && b < aEnd) {
6799         PetscCall(PetscSectionGetDof(aSec,b,&bDof));
6800       }
6801       if (bDof) {
6802         /* this point is constrained */
6803         /* it is going to be replaced by its anchors */
6804         PetscInt bOff, q;
6805 
6806         anyConstrained = PETSC_TRUE;
6807         newNumPoints  += bDof;
6808         PetscCall(PetscSectionGetOffset(aSec,b,&bOff));
6809         for (q = 0; q < bDof; q++) {
6810           PetscInt a = anchors[bOff + q];
6811           PetscInt aDof;
6812 
6813           PetscCall(PetscSectionGetDof(section,a,&aDof));
6814           newNumIndices += aDof;
6815           for (f = 0; f < numFields; ++f) {
6816             PetscInt fDof;
6817 
6818             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
6819             newOffsets[f+1] += fDof;
6820           }
6821         }
6822       }
6823       else {
6824         /* this point is not constrained */
6825         newNumPoints++;
6826         newNumIndices += bSecDof;
6827         for (f = 0; f < numFields; ++f) {
6828           PetscInt fDof;
6829 
6830           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6831           newOffsets[f+1] += fDof;
6832         }
6833       }
6834     }
6835   }
6836   if (!anyConstrained) {
6837     if (outNumPoints)  *outNumPoints  = 0;
6838     if (outNumIndices) *outNumIndices = 0;
6839     if (outPoints)     *outPoints     = NULL;
6840     if (outValues)     *outValues     = NULL;
6841     if (aSec) PetscCall(ISRestoreIndices(aIS,&anchors));
6842     PetscFunctionReturn(0);
6843   }
6844 
6845   if (outNumPoints)  *outNumPoints  = newNumPoints;
6846   if (outNumIndices) *outNumIndices = newNumIndices;
6847 
6848   for (f = 0; f < numFields; ++f) newOffsets[f+1] += newOffsets[f];
6849 
6850   if (!outPoints && !outValues) {
6851     if (offsets) {
6852       for (f = 0; f <= numFields; f++) {
6853         offsets[f] = newOffsets[f];
6854       }
6855     }
6856     if (aSec) PetscCall(ISRestoreIndices(aIS,&anchors));
6857     PetscFunctionReturn(0);
6858   }
6859 
6860   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
6861 
6862   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
6863 
6864   /* workspaces */
6865   if (numFields) {
6866     for (f = 0; f < numFields; f++) {
6867       PetscCall(DMGetWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[f]));
6868       PetscCall(DMGetWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[f]));
6869     }
6870   }
6871   else {
6872     PetscCall(DMGetWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[0]));
6873     PetscCall(DMGetWorkArray(dm,numPoints,MPIU_INT,&newPointOffsets[0]));
6874   }
6875 
6876   /* get workspaces for the point-to-point matrices */
6877   if (numFields) {
6878     PetscInt totalOffset, totalMatOffset;
6879 
6880     for (p = 0; p < numPoints; p++) {
6881       PetscInt b    = points[2*p];
6882       PetscInt bDof = 0, bSecDof;
6883 
6884       PetscCall(PetscSectionGetDof(section,b,&bSecDof));
6885       if (!bSecDof) {
6886         for (f = 0; f < numFields; f++) {
6887           newPointOffsets[f][p + 1] = 0;
6888           pointMatOffsets[f][p + 1] = 0;
6889         }
6890         continue;
6891       }
6892       if (b >= aStart && b < aEnd) {
6893         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6894       }
6895       if (bDof) {
6896         for (f = 0; f < numFields; f++) {
6897           PetscInt fDof, q, bOff, allFDof = 0;
6898 
6899           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6900           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6901           for (q = 0; q < bDof; q++) {
6902             PetscInt a = anchors[bOff + q];
6903             PetscInt aFDof;
6904 
6905             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
6906             allFDof += aFDof;
6907           }
6908           newPointOffsets[f][p+1] = allFDof;
6909           pointMatOffsets[f][p+1] = fDof * allFDof;
6910         }
6911       }
6912       else {
6913         for (f = 0; f < numFields; f++) {
6914           PetscInt fDof;
6915 
6916           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6917           newPointOffsets[f][p+1] = fDof;
6918           pointMatOffsets[f][p+1] = 0;
6919         }
6920       }
6921     }
6922     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
6923       newPointOffsets[f][0] = totalOffset;
6924       pointMatOffsets[f][0] = totalMatOffset;
6925       for (p = 0; p < numPoints; p++) {
6926         newPointOffsets[f][p+1] += newPointOffsets[f][p];
6927         pointMatOffsets[f][p+1] += pointMatOffsets[f][p];
6928       }
6929       totalOffset    = newPointOffsets[f][numPoints];
6930       totalMatOffset = pointMatOffsets[f][numPoints];
6931       PetscCall(DMGetWorkArray(dm,pointMatOffsets[f][numPoints],MPIU_SCALAR,&pointMat[f]));
6932     }
6933   }
6934   else {
6935     for (p = 0; p < numPoints; p++) {
6936       PetscInt b    = points[2*p];
6937       PetscInt bDof = 0, bSecDof;
6938 
6939       PetscCall(PetscSectionGetDof(section,b,&bSecDof));
6940       if (!bSecDof) {
6941         newPointOffsets[0][p + 1] = 0;
6942         pointMatOffsets[0][p + 1] = 0;
6943         continue;
6944       }
6945       if (b >= aStart && b < aEnd) {
6946         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6947       }
6948       if (bDof) {
6949         PetscInt bOff, q, allDof = 0;
6950 
6951         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6952         for (q = 0; q < bDof; q++) {
6953           PetscInt a = anchors[bOff + q], aDof;
6954 
6955           PetscCall(PetscSectionGetDof(section, a, &aDof));
6956           allDof += aDof;
6957         }
6958         newPointOffsets[0][p+1] = allDof;
6959         pointMatOffsets[0][p+1] = bSecDof * allDof;
6960       }
6961       else {
6962         newPointOffsets[0][p+1] = bSecDof;
6963         pointMatOffsets[0][p+1] = 0;
6964       }
6965     }
6966     newPointOffsets[0][0] = 0;
6967     pointMatOffsets[0][0] = 0;
6968     for (p = 0; p < numPoints; p++) {
6969       newPointOffsets[0][p+1] += newPointOffsets[0][p];
6970       pointMatOffsets[0][p+1] += pointMatOffsets[0][p];
6971     }
6972     PetscCall(DMGetWorkArray(dm,pointMatOffsets[0][numPoints],MPIU_SCALAR,&pointMat[0]));
6973   }
6974 
6975   /* output arrays */
6976   PetscCall(DMGetWorkArray(dm,2*newNumPoints,MPIU_INT,&newPoints));
6977 
6978   /* get the point-to-point matrices; construct newPoints */
6979   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
6980   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
6981   PetscCall(DMGetWorkArray(dm,maxDof,MPIU_INT,&indices));
6982   PetscCall(DMGetWorkArray(dm,maxAnchor*maxDof,MPIU_INT,&newIndices));
6983   if (numFields) {
6984     for (p = 0, newP = 0; p < numPoints; p++) {
6985       PetscInt b    = points[2*p];
6986       PetscInt o    = points[2*p+1];
6987       PetscInt bDof = 0, bSecDof;
6988 
6989       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
6990       if (!bSecDof) {
6991         continue;
6992       }
6993       if (b >= aStart && b < aEnd) {
6994         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6995       }
6996       if (bDof) {
6997         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
6998 
6999         fStart[0] = 0;
7000         fEnd[0]   = 0;
7001         for (f = 0; f < numFields; f++) {
7002           PetscInt fDof;
7003 
7004           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
7005           fStart[f+1] = fStart[f] + fDof;
7006           fEnd[f+1]   = fStart[f+1];
7007         }
7008         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7009         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7010 
7011         fAnchorStart[0] = 0;
7012         fAnchorEnd[0]   = 0;
7013         for (f = 0; f < numFields; f++) {
7014           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7015 
7016           fAnchorStart[f+1] = fAnchorStart[f] + fDof;
7017           fAnchorEnd[f+1]   = fAnchorStart[f + 1];
7018         }
7019         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7020         for (q = 0; q < bDof; q++) {
7021           PetscInt a = anchors[bOff + q], aOff;
7022 
7023           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7024           newPoints[2*(newP + q)]     = a;
7025           newPoints[2*(newP + q) + 1] = 0;
7026           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7027           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7028         }
7029         newP += bDof;
7030 
7031         if (outValues) {
7032           /* get the point-to-point submatrix */
7033           for (f = 0; f < numFields; f++) {
7034             PetscCall(MatGetValues(cMat,fEnd[f]-fStart[f],indices + fStart[f],fAnchorEnd[f] - fAnchorStart[f],newIndices + fAnchorStart[f],pointMat[f] + pointMatOffsets[f][p]));
7035           }
7036         }
7037       }
7038       else {
7039         newPoints[2 * newP]     = b;
7040         newPoints[2 * newP + 1] = o;
7041         newP++;
7042       }
7043     }
7044   } else {
7045     for (p = 0; p < numPoints; p++) {
7046       PetscInt b    = points[2*p];
7047       PetscInt o    = points[2*p+1];
7048       PetscInt bDof = 0, bSecDof;
7049 
7050       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7051       if (!bSecDof) {
7052         continue;
7053       }
7054       if (b >= aStart && b < aEnd) {
7055         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7056       }
7057       if (bDof) {
7058         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7059 
7060         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7061         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7062 
7063         PetscCall(PetscSectionGetOffset (aSec, b, &bOff));
7064         for (q = 0; q < bDof; q++) {
7065           PetscInt a = anchors[bOff + q], aOff;
7066 
7067           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7068 
7069           newPoints[2*(newP + q)]     = a;
7070           newPoints[2*(newP + q) + 1] = 0;
7071           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7072           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7073         }
7074         newP += bDof;
7075 
7076         /* get the point-to-point submatrix */
7077         if (outValues) {
7078           PetscCall(MatGetValues(cMat,bEnd,indices,bAnchorEnd,newIndices,pointMat[0] + pointMatOffsets[0][p]));
7079         }
7080       }
7081       else {
7082         newPoints[2 * newP]     = b;
7083         newPoints[2 * newP + 1] = o;
7084         newP++;
7085       }
7086     }
7087   }
7088 
7089   if (outValues) {
7090     PetscCall(DMGetWorkArray(dm,newNumIndices*numIndices,MPIU_SCALAR,&tmpValues));
7091     PetscCall(PetscArrayzero(tmpValues,newNumIndices*numIndices));
7092     /* multiply constraints on the right */
7093     if (numFields) {
7094       for (f = 0; f < numFields; f++) {
7095         PetscInt oldOff = offsets[f];
7096 
7097         for (p = 0; p < numPoints; p++) {
7098           PetscInt cStart = newPointOffsets[f][p];
7099           PetscInt b      = points[2 * p];
7100           PetscInt c, r, k;
7101           PetscInt dof;
7102 
7103           PetscCall(PetscSectionGetFieldDof(section,b,f,&dof));
7104           if (!dof) {
7105             continue;
7106           }
7107           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7108             PetscInt nCols         = newPointOffsets[f][p+1]-cStart;
7109             const PetscScalar *mat = pointMat[f] + pointMatOffsets[f][p];
7110 
7111             for (r = 0; r < numIndices; r++) {
7112               for (c = 0; c < nCols; c++) {
7113                 for (k = 0; k < dof; k++) {
7114                   tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7115                 }
7116               }
7117             }
7118           }
7119           else {
7120             /* copy this column as is */
7121             for (r = 0; r < numIndices; r++) {
7122               for (c = 0; c < dof; c++) {
7123                 tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7124               }
7125             }
7126           }
7127           oldOff += dof;
7128         }
7129       }
7130     }
7131     else {
7132       PetscInt oldOff = 0;
7133       for (p = 0; p < numPoints; p++) {
7134         PetscInt cStart = newPointOffsets[0][p];
7135         PetscInt b      = points[2 * p];
7136         PetscInt c, r, k;
7137         PetscInt dof;
7138 
7139         PetscCall(PetscSectionGetDof(section,b,&dof));
7140         if (!dof) {
7141           continue;
7142         }
7143         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7144           PetscInt nCols         = newPointOffsets[0][p+1]-cStart;
7145           const PetscScalar *mat = pointMat[0] + pointMatOffsets[0][p];
7146 
7147           for (r = 0; r < numIndices; r++) {
7148             for (c = 0; c < nCols; c++) {
7149               for (k = 0; k < dof; k++) {
7150                 tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7151               }
7152             }
7153           }
7154         }
7155         else {
7156           /* copy this column as is */
7157           for (r = 0; r < numIndices; r++) {
7158             for (c = 0; c < dof; c++) {
7159               tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7160             }
7161           }
7162         }
7163         oldOff += dof;
7164       }
7165     }
7166 
7167     if (multiplyLeft) {
7168       PetscCall(DMGetWorkArray(dm,newNumIndices*newNumIndices,MPIU_SCALAR,&newValues));
7169       PetscCall(PetscArrayzero(newValues,newNumIndices*newNumIndices));
7170       /* multiply constraints transpose on the left */
7171       if (numFields) {
7172         for (f = 0; f < numFields; f++) {
7173           PetscInt oldOff = offsets[f];
7174 
7175           for (p = 0; p < numPoints; p++) {
7176             PetscInt rStart = newPointOffsets[f][p];
7177             PetscInt b      = points[2 * p];
7178             PetscInt c, r, k;
7179             PetscInt dof;
7180 
7181             PetscCall(PetscSectionGetFieldDof(section,b,f,&dof));
7182             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7183               PetscInt nRows                        = newPointOffsets[f][p+1]-rStart;
7184               const PetscScalar *PETSC_RESTRICT mat = pointMat[f] + pointMatOffsets[f][p];
7185 
7186               for (r = 0; r < nRows; r++) {
7187                 for (c = 0; c < newNumIndices; c++) {
7188                   for (k = 0; k < dof; k++) {
7189                     newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7190                   }
7191                 }
7192               }
7193             }
7194             else {
7195               /* copy this row as is */
7196               for (r = 0; r < dof; r++) {
7197                 for (c = 0; c < newNumIndices; c++) {
7198                   newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7199                 }
7200               }
7201             }
7202             oldOff += dof;
7203           }
7204         }
7205       }
7206       else {
7207         PetscInt oldOff = 0;
7208 
7209         for (p = 0; p < numPoints; p++) {
7210           PetscInt rStart = newPointOffsets[0][p];
7211           PetscInt b      = points[2 * p];
7212           PetscInt c, r, k;
7213           PetscInt dof;
7214 
7215           PetscCall(PetscSectionGetDof(section,b,&dof));
7216           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7217             PetscInt nRows                        = newPointOffsets[0][p+1]-rStart;
7218             const PetscScalar *PETSC_RESTRICT mat = pointMat[0] + pointMatOffsets[0][p];
7219 
7220             for (r = 0; r < nRows; r++) {
7221               for (c = 0; c < newNumIndices; c++) {
7222                 for (k = 0; k < dof; k++) {
7223                   newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7224                 }
7225               }
7226             }
7227           }
7228           else {
7229             /* copy this row as is */
7230             for (r = 0; r < dof; r++) {
7231               for (c = 0; c < newNumIndices; c++) {
7232                 newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7233               }
7234             }
7235           }
7236           oldOff += dof;
7237         }
7238       }
7239 
7240       PetscCall(DMRestoreWorkArray(dm,newNumIndices*numIndices,MPIU_SCALAR,&tmpValues));
7241     }
7242     else {
7243       newValues = tmpValues;
7244     }
7245   }
7246 
7247   /* clean up */
7248   PetscCall(DMRestoreWorkArray(dm,maxDof,MPIU_INT,&indices));
7249   PetscCall(DMRestoreWorkArray(dm,maxAnchor*maxDof,MPIU_INT,&newIndices));
7250 
7251   if (numFields) {
7252     for (f = 0; f < numFields; f++) {
7253       PetscCall(DMRestoreWorkArray(dm,pointMatOffsets[f][numPoints],MPIU_SCALAR,&pointMat[f]));
7254       PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[f]));
7255       PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[f]));
7256     }
7257   }
7258   else {
7259     PetscCall(DMRestoreWorkArray(dm,pointMatOffsets[0][numPoints],MPIU_SCALAR,&pointMat[0]));
7260     PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[0]));
7261     PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[0]));
7262   }
7263   PetscCall(ISRestoreIndices(aIS,&anchors));
7264 
7265   /* output */
7266   if (outPoints) {
7267     *outPoints = newPoints;
7268   }
7269   else {
7270     PetscCall(DMRestoreWorkArray(dm,2*newNumPoints,MPIU_INT,&newPoints));
7271   }
7272   if (outValues) {
7273     *outValues = newValues;
7274   }
7275   for (f = 0; f <= numFields; f++) {
7276     offsets[f] = newOffsets[f];
7277   }
7278   PetscFunctionReturn(0);
7279 }
7280 
7281 /*@C
7282   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7283 
7284   Not collective
7285 
7286   Input Parameters:
7287 + dm         - The DM
7288 . section    - The PetscSection describing the points (a local section)
7289 . idxSection - The PetscSection from which to obtain indices (may be local or global)
7290 . point      - The point defining the closure
7291 - useClPerm  - Use the closure point permutation if available
7292 
7293   Output Parameters:
7294 + numIndices - The number of dof indices in the closure of point with the input sections
7295 . indices    - The dof indices
7296 . outOffsets - Array to write the field offsets into, or NULL
7297 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7298 
7299   Notes:
7300   Must call DMPlexRestoreClosureIndices() to free allocated memory
7301 
7302   If idxSection is global, any constrained dofs (see DMAddBoundary(), for example) will get negative indices.  The value
7303   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7304   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7305   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7306   indices (with the above semantics) are implied.
7307 
7308   Level: advanced
7309 
7310 .seealso `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7311 @*/
7312 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm,
7313                                        PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7314 {
7315   /* Closure ordering */
7316   PetscSection        clSection;
7317   IS                  clPoints;
7318   const PetscInt     *clp;
7319   PetscInt           *points;
7320   const PetscInt     *clperm = NULL;
7321   /* Dof permutation and sign flips */
7322   const PetscInt    **perms[32] = {NULL};
7323   const PetscScalar **flips[32] = {NULL};
7324   PetscScalar        *valCopy   = NULL;
7325   /* Hanging node constraints */
7326   PetscInt           *pointsC = NULL;
7327   PetscScalar        *valuesC = NULL;
7328   PetscInt            NclC, NiC;
7329 
7330   PetscInt           *idx;
7331   PetscInt            Nf, Ncl, Ni = 0, offsets[32], p, f;
7332   PetscBool           isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7333 
7334   PetscFunctionBeginHot;
7335   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7336   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7337   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7338   if (numIndices) PetscValidIntPointer(numIndices, 6);
7339   if (indices)    PetscValidPointer(indices, 7);
7340   if (outOffsets) PetscValidIntPointer(outOffsets, 8);
7341   if (values)     PetscValidPointer(values, 9);
7342   PetscCall(PetscSectionGetNumFields(section, &Nf));
7343   PetscCheck(Nf <= 31,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7344   PetscCall(PetscArrayzero(offsets, 32));
7345   /* 1) Get points in closure */
7346   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7347   if (useClPerm) {
7348     PetscInt depth, clsize;
7349     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7350     for (clsize=0,p=0; p<Ncl; p++) {
7351       PetscInt dof;
7352       PetscCall(PetscSectionGetDof(section, points[2*p], &dof));
7353       clsize += dof;
7354     }
7355     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, clsize, &clperm));
7356   }
7357   /* 2) Get number of indices on these points and field offsets from section */
7358   for (p = 0; p < Ncl*2; p += 2) {
7359     PetscInt dof, fdof;
7360 
7361     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7362     for (f = 0; f < Nf; ++f) {
7363       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7364       offsets[f+1] += fdof;
7365     }
7366     Ni += dof;
7367   }
7368   for (f = 1; f < Nf; ++f) offsets[f+1] += offsets[f];
7369   PetscCheck(!Nf || offsets[Nf] == Ni,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7370   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7371   for (f = 0; f < PetscMax(1, Nf); ++f) {
7372     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7373     else    PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7374     /* may need to apply sign changes to the element matrix */
7375     if (values && flips[f]) {
7376       PetscInt foffset = offsets[f];
7377 
7378       for (p = 0; p < Ncl; ++p) {
7379         PetscInt           pnt  = points[2*p], fdof;
7380         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7381 
7382         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7383         else     PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7384         if (flip) {
7385           PetscInt i, j, k;
7386 
7387           if (!valCopy) {
7388             PetscCall(DMGetWorkArray(dm, Ni*Ni, MPIU_SCALAR, &valCopy));
7389             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7390             *values = valCopy;
7391           }
7392           for (i = 0; i < fdof; ++i) {
7393             PetscScalar fval = flip[i];
7394 
7395             for (k = 0; k < Ni; ++k) {
7396               valCopy[Ni * (foffset + i) + k] *= fval;
7397               valCopy[Ni * k + (foffset + i)] *= fval;
7398             }
7399           }
7400         }
7401         foffset += fdof;
7402       }
7403     }
7404   }
7405   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7406   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7407   if (NclC) {
7408     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni*Ni, MPIU_SCALAR, &valCopy));
7409     for (f = 0; f < PetscMax(1, Nf); ++f) {
7410       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7411       else    PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7412     }
7413     for (f = 0; f < PetscMax(1, Nf); ++f) {
7414       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7415       else    PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7416     }
7417     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7418     Ncl     = NclC;
7419     Ni      = NiC;
7420     points  = pointsC;
7421     if (values) *values = valuesC;
7422   }
7423   /* 5) Calculate indices */
7424   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7425   if (Nf) {
7426     PetscInt  idxOff;
7427     PetscBool useFieldOffsets;
7428 
7429     if (outOffsets) {for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];}
7430     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7431     if (useFieldOffsets) {
7432       for (p = 0; p < Ncl; ++p) {
7433         const PetscInt pnt = points[p*2];
7434 
7435         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7436       }
7437     } else {
7438       for (p = 0; p < Ncl; ++p) {
7439         const PetscInt pnt = points[p*2];
7440 
7441         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7442         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7443          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7444          * global section. */
7445         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff+1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7446       }
7447     }
7448   } else {
7449     PetscInt off = 0, idxOff;
7450 
7451     for (p = 0; p < Ncl; ++p) {
7452       const PetscInt  pnt  = points[p*2];
7453       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7454 
7455       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7456       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7457        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7458       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff+1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7459     }
7460   }
7461   /* 6) Cleanup */
7462   for (f = 0; f < PetscMax(1, Nf); ++f) {
7463     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7464     else    PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7465   }
7466   if (NclC) {
7467     PetscCall(DMRestoreWorkArray(dm, NclC*2, MPIU_INT, &pointsC));
7468   } else {
7469     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7470   }
7471 
7472   if (numIndices) *numIndices = Ni;
7473   if (indices)    *indices    = idx;
7474   PetscFunctionReturn(0);
7475 }
7476 
7477 /*@C
7478   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7479 
7480   Not collective
7481 
7482   Input Parameters:
7483 + dm         - The DM
7484 . section    - The PetscSection describing the points (a local section)
7485 . idxSection - The PetscSection from which to obtain indices (may be local or global)
7486 . point      - The point defining the closure
7487 - useClPerm  - Use the closure point permutation if available
7488 
7489   Output Parameters:
7490 + numIndices - The number of dof indices in the closure of point with the input sections
7491 . indices    - The dof indices
7492 . outOffsets - Array to write the field offsets into, or NULL
7493 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7494 
7495   Notes:
7496   If values were modified, the user is responsible for calling DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values).
7497 
7498   If idxSection is global, any constrained dofs (see DMAddBoundary(), for example) will get negative indices.  The value
7499   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7500   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7501   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7502   indices (with the above semantics) are implied.
7503 
7504   Level: advanced
7505 
7506 .seealso `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7507 @*/
7508 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm,
7509                                            PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7510 {
7511   PetscFunctionBegin;
7512   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7513   PetscValidPointer(indices, 7);
7514   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
7515   PetscFunctionReturn(0);
7516 }
7517 
7518 /*@C
7519   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
7520 
7521   Not collective
7522 
7523   Input Parameters:
7524 + dm - The DM
7525 . section - The section describing the layout in v, or NULL to use the default section
7526 . globalSection - The section describing the layout in v, or NULL to use the default global section
7527 . A - The matrix
7528 . point - The point in the DM
7529 . values - The array of values
7530 - mode - The insert mode, where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions
7531 
7532   Fortran Notes:
7533   This routine is only available in Fortran 90, and you must include petsc.h90 in your code.
7534 
7535   Level: intermediate
7536 
7537 .seealso `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7538 @*/
7539 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7540 {
7541   DM_Plex           *mesh = (DM_Plex*) dm->data;
7542   PetscInt          *indices;
7543   PetscInt           numIndices;
7544   const PetscScalar *valuesOrig = values;
7545   PetscErrorCode     ierr;
7546 
7547   PetscFunctionBegin;
7548   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7549   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7550   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7551   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
7552   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
7553   PetscValidHeaderSpecific(A, MAT_CLASSID, 4);
7554 
7555   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **) &values));
7556 
7557   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
7558   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7559   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
7560   if (ierr) {
7561     PetscMPIInt    rank;
7562 
7563     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7564     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7565     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
7566     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **) &values));
7567     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7568     SETERRQ(PetscObjectComm((PetscObject)dm),ierr,"Not possible to set matrix values");
7569   }
7570   if (mesh->printFEM > 1) {
7571     PetscInt i;
7572     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
7573     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
7574     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
7575   }
7576 
7577   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **) &values));
7578   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7579   PetscFunctionReturn(0);
7580 }
7581 
7582 /*@C
7583   DMPlexMatSetClosure - Set an array of the values on the closure of 'point' using a different row and column section
7584 
7585   Not collective
7586 
7587   Input Parameters:
7588 + dmRow - The DM for the row fields
7589 . sectionRow - The section describing the layout, or NULL to use the default section in dmRow
7590 . globalSectionRow - The section describing the layout, or NULL to use the default global section in dmRow
7591 . dmCol - The DM for the column fields
7592 . sectionCol - The section describing the layout, or NULL to use the default section in dmCol
7593 . globalSectionCol - The section describing the layout, or NULL to use the default global section in dmCol
7594 . A - The matrix
7595 . point - The point in the DMs
7596 . values - The array of values
7597 - mode - The insert mode, where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions
7598 
7599   Level: intermediate
7600 
7601 .seealso `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7602 @*/
7603 PetscErrorCode DMPlexMatSetClosureGeneral(DM dmRow, PetscSection sectionRow, PetscSection globalSectionRow, DM dmCol, PetscSection sectionCol, PetscSection globalSectionCol, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7604 {
7605   DM_Plex           *mesh = (DM_Plex*) dmRow->data;
7606   PetscInt          *indicesRow, *indicesCol;
7607   PetscInt           numIndicesRow, numIndicesCol;
7608   const PetscScalar *valuesOrig = values;
7609   PetscErrorCode     ierr;
7610 
7611   PetscFunctionBegin;
7612   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
7613   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
7614   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
7615   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
7616   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
7617   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 4);
7618   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
7619   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 5);
7620   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
7621   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 6);
7622   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7623 
7624   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values));
7625   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **) &values));
7626 
7627   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7628   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7629   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
7630   if (ierr) {
7631     PetscMPIInt    rank;
7632 
7633     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7634     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7635     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7636     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values));
7637     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **) &values));
7638     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7639   }
7640 
7641   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values));
7642   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **) &values));
7643   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7644   PetscFunctionReturn(0);
7645 }
7646 
7647 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7648 {
7649   DM_Plex        *mesh   = (DM_Plex*) dmf->data;
7650   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7651   PetscInt       *cpoints = NULL;
7652   PetscInt       *findices, *cindices;
7653   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7654   PetscInt        foffsets[32], coffsets[32];
7655   DMPolytopeType  ct;
7656   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7657   PetscErrorCode  ierr;
7658 
7659   PetscFunctionBegin;
7660   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7661   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7662   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7663   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7664   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7665   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7666   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7667   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7668   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7669   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7670   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7671   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7672   PetscCheck(numFields <= 31,PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7673   PetscCall(PetscArrayzero(foffsets, 32));
7674   PetscCall(PetscArrayzero(coffsets, 32));
7675   /* Column indices */
7676   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7677   maxFPoints = numCPoints;
7678   /* Compress out points not in the section */
7679   /*   TODO: Squeeze out points with 0 dof as well */
7680   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7681   for (p = 0, q = 0; p < numCPoints*2; p += 2) {
7682     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7683       cpoints[q*2]   = cpoints[p];
7684       cpoints[q*2+1] = cpoints[p+1];
7685       ++q;
7686     }
7687   }
7688   numCPoints = q;
7689   for (p = 0, numCIndices = 0; p < numCPoints*2; p += 2) {
7690     PetscInt fdof;
7691 
7692     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7693     if (!dof) continue;
7694     for (f = 0; f < numFields; ++f) {
7695       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7696       coffsets[f+1] += fdof;
7697     }
7698     numCIndices += dof;
7699   }
7700   for (f = 1; f < numFields; ++f) coffsets[f+1] += coffsets[f];
7701   /* Row indices */
7702   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7703   {
7704     DMPlexTransform tr;
7705     DMPolytopeType *rct;
7706     PetscInt       *rsize, *rcone, *rornt, Nt;
7707 
7708     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7709     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7710     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7711     numSubcells = rsize[Nt-1];
7712     PetscCall(DMPlexTransformDestroy(&tr));
7713   }
7714   PetscCall(DMGetWorkArray(dmf, maxFPoints*2*numSubcells, MPIU_INT, &ftotpoints));
7715   for (r = 0, q = 0; r < numSubcells; ++r) {
7716     /* TODO Map from coarse to fine cells */
7717     PetscCall(DMPlexGetTransitiveClosure(dmf, point*numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7718     /* Compress out points not in the section */
7719     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7720     for (p = 0; p < numFPoints*2; p += 2) {
7721       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7722         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7723         if (!dof) continue;
7724         for (s = 0; s < q; ++s) if (fpoints[p] == ftotpoints[s*2]) break;
7725         if (s < q) continue;
7726         ftotpoints[q*2]   = fpoints[p];
7727         ftotpoints[q*2+1] = fpoints[p+1];
7728         ++q;
7729       }
7730     }
7731     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7732   }
7733   numFPoints = q;
7734   for (p = 0, numFIndices = 0; p < numFPoints*2; p += 2) {
7735     PetscInt fdof;
7736 
7737     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
7738     if (!dof) continue;
7739     for (f = 0; f < numFields; ++f) {
7740       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
7741       foffsets[f+1] += fdof;
7742     }
7743     numFIndices += dof;
7744   }
7745   for (f = 1; f < numFields; ++f) foffsets[f+1] += foffsets[f];
7746 
7747   PetscCheck(!numFields || foffsets[numFields] == numFIndices,PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
7748   PetscCheck(!numFields || coffsets[numFields] == numCIndices,PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
7749   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7750   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7751   if (numFields) {
7752     const PetscInt **permsF[32] = {NULL};
7753     const PetscInt **permsC[32] = {NULL};
7754 
7755     for (f = 0; f < numFields; f++) {
7756       PetscCall(PetscSectionGetFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7757       PetscCall(PetscSectionGetFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7758     }
7759     for (p = 0; p < numFPoints; p++) {
7760       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7761       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
7762     }
7763     for (p = 0; p < numCPoints; p++) {
7764       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7765       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
7766     }
7767     for (f = 0; f < numFields; f++) {
7768       PetscCall(PetscSectionRestoreFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7769       PetscCall(PetscSectionRestoreFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7770     }
7771   } else {
7772     const PetscInt **permsF = NULL;
7773     const PetscInt **permsC = NULL;
7774 
7775     PetscCall(PetscSectionGetPointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7776     PetscCall(PetscSectionGetPointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7777     for (p = 0, off = 0; p < numFPoints; p++) {
7778       const PetscInt *perm = permsF ? permsF[p] : NULL;
7779 
7780       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7781       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
7782     }
7783     for (p = 0, off = 0; p < numCPoints; p++) {
7784       const PetscInt *perm = permsC ? permsC[p] : NULL;
7785 
7786       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7787       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
7788     }
7789     PetscCall(PetscSectionRestorePointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7790     PetscCall(PetscSectionRestorePointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7791   }
7792   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
7793   /* TODO: flips */
7794   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7795   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
7796   if (ierr) {
7797     PetscMPIInt    rank;
7798 
7799     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7800     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7801     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
7802     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7803     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7804   }
7805   PetscCall(DMRestoreWorkArray(dmf, numCPoints*2*4, MPIU_INT, &ftotpoints));
7806   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7807   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7808   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7809   PetscFunctionReturn(0);
7810 }
7811 
7812 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
7813 {
7814   PetscInt      *fpoints = NULL, *ftotpoints = NULL;
7815   PetscInt      *cpoints = NULL;
7816   PetscInt       foffsets[32], coffsets[32];
7817   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7818   DMPolytopeType ct;
7819   PetscInt       numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7820 
7821   PetscFunctionBegin;
7822   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7823   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7824   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7825   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7826   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7827   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7828   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7829   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7830   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7831   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7832   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7833   PetscCheck(numFields <= 31,PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7834   PetscCall(PetscArrayzero(foffsets, 32));
7835   PetscCall(PetscArrayzero(coffsets, 32));
7836   /* Column indices */
7837   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7838   maxFPoints = numCPoints;
7839   /* Compress out points not in the section */
7840   /*   TODO: Squeeze out points with 0 dof as well */
7841   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7842   for (p = 0, q = 0; p < numCPoints*2; p += 2) {
7843     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7844       cpoints[q*2]   = cpoints[p];
7845       cpoints[q*2+1] = cpoints[p+1];
7846       ++q;
7847     }
7848   }
7849   numCPoints = q;
7850   for (p = 0, numCIndices = 0; p < numCPoints*2; p += 2) {
7851     PetscInt fdof;
7852 
7853     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7854     if (!dof) continue;
7855     for (f = 0; f < numFields; ++f) {
7856       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7857       coffsets[f+1] += fdof;
7858     }
7859     numCIndices += dof;
7860   }
7861   for (f = 1; f < numFields; ++f) coffsets[f+1] += coffsets[f];
7862   /* Row indices */
7863   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7864   {
7865     DMPlexTransform tr;
7866     DMPolytopeType *rct;
7867     PetscInt       *rsize, *rcone, *rornt, Nt;
7868 
7869     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7870     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7871     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7872     numSubcells = rsize[Nt-1];
7873     PetscCall(DMPlexTransformDestroy(&tr));
7874   }
7875   PetscCall(DMGetWorkArray(dmf, maxFPoints*2*numSubcells, MPIU_INT, &ftotpoints));
7876   for (r = 0, q = 0; r < numSubcells; ++r) {
7877     /* TODO Map from coarse to fine cells */
7878     PetscCall(DMPlexGetTransitiveClosure(dmf, point*numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7879     /* Compress out points not in the section */
7880     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7881     for (p = 0; p < numFPoints*2; p += 2) {
7882       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7883         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7884         if (!dof) continue;
7885         for (s = 0; s < q; ++s) if (fpoints[p] == ftotpoints[s*2]) break;
7886         if (s < q) continue;
7887         ftotpoints[q*2]   = fpoints[p];
7888         ftotpoints[q*2+1] = fpoints[p+1];
7889         ++q;
7890       }
7891     }
7892     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7893   }
7894   numFPoints = q;
7895   for (p = 0, numFIndices = 0; p < numFPoints*2; p += 2) {
7896     PetscInt fdof;
7897 
7898     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
7899     if (!dof) continue;
7900     for (f = 0; f < numFields; ++f) {
7901       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
7902       foffsets[f+1] += fdof;
7903     }
7904     numFIndices += dof;
7905   }
7906   for (f = 1; f < numFields; ++f) foffsets[f+1] += foffsets[f];
7907 
7908   PetscCheck(!numFields || foffsets[numFields] == numFIndices,PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
7909   PetscCheck(!numFields || coffsets[numFields] == numCIndices,PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
7910   if (numFields) {
7911     const PetscInt **permsF[32] = {NULL};
7912     const PetscInt **permsC[32] = {NULL};
7913 
7914     for (f = 0; f < numFields; f++) {
7915       PetscCall(PetscSectionGetFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7916       PetscCall(PetscSectionGetFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7917     }
7918     for (p = 0; p < numFPoints; p++) {
7919       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7920       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
7921     }
7922     for (p = 0; p < numCPoints; p++) {
7923       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7924       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
7925     }
7926     for (f = 0; f < numFields; f++) {
7927       PetscCall(PetscSectionRestoreFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7928       PetscCall(PetscSectionRestoreFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7929     }
7930   } else {
7931     const PetscInt **permsF = NULL;
7932     const PetscInt **permsC = NULL;
7933 
7934     PetscCall(PetscSectionGetPointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7935     PetscCall(PetscSectionGetPointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7936     for (p = 0, off = 0; p < numFPoints; p++) {
7937       const PetscInt *perm = permsF ? permsF[p] : NULL;
7938 
7939       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7940       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
7941     }
7942     for (p = 0, off = 0; p < numCPoints; p++) {
7943       const PetscInt *perm = permsC ? permsC[p] : NULL;
7944 
7945       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7946       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
7947     }
7948     PetscCall(PetscSectionRestorePointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7949     PetscCall(PetscSectionRestorePointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7950   }
7951   PetscCall(DMRestoreWorkArray(dmf, numCPoints*2*4, MPIU_INT, &ftotpoints));
7952   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7953   PetscFunctionReturn(0);
7954 }
7955 
7956 /*@C
7957   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
7958 
7959   Input Parameter:
7960 . dm   - The DMPlex object
7961 
7962   Output Parameter:
7963 . cellHeight - The height of a cell
7964 
7965   Level: developer
7966 
7967 .seealso `DMPlexSetVTKCellHeight()`
7968 @*/
7969 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
7970 {
7971   DM_Plex *mesh = (DM_Plex*) dm->data;
7972 
7973   PetscFunctionBegin;
7974   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7975   PetscValidIntPointer(cellHeight, 2);
7976   *cellHeight = mesh->vtkCellHeight;
7977   PetscFunctionReturn(0);
7978 }
7979 
7980 /*@C
7981   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
7982 
7983   Input Parameters:
7984 + dm   - The DMPlex object
7985 - cellHeight - The height of a cell
7986 
7987   Level: developer
7988 
7989 .seealso `DMPlexGetVTKCellHeight()`
7990 @*/
7991 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
7992 {
7993   DM_Plex *mesh = (DM_Plex*) dm->data;
7994 
7995   PetscFunctionBegin;
7996   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7997   mesh->vtkCellHeight = cellHeight;
7998   PetscFunctionReturn(0);
7999 }
8000 
8001 /*@
8002   DMPlexGetGhostCellStratum - Get the range of cells which are used to enforce FV boundary conditions
8003 
8004   Input Parameter:
8005 . dm - The DMPlex object
8006 
8007   Output Parameters:
8008 + gcStart - The first ghost cell, or NULL
8009 - gcEnd   - The upper bound on ghost cells, or NULL
8010 
8011   Level: advanced
8012 
8013 .seealso `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
8014 @*/
8015 PetscErrorCode DMPlexGetGhostCellStratum(DM dm, PetscInt *gcStart, PetscInt *gcEnd)
8016 {
8017   DMLabel        ctLabel;
8018 
8019   PetscFunctionBegin;
8020   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8021   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
8022   PetscCall(DMLabelGetStratumBounds(ctLabel, DM_POLYTOPE_FV_GHOST, gcStart, gcEnd));
8023   PetscFunctionReturn(0);
8024 }
8025 
8026 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8027 {
8028   PetscSection   section, globalSection;
8029   PetscInt      *numbers, p;
8030 
8031   PetscFunctionBegin;
8032   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf));
8033   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8034   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8035   for (p = pStart; p < pEnd; ++p) {
8036     PetscCall(PetscSectionSetDof(section, p, 1));
8037   }
8038   PetscCall(PetscSectionSetUp(section));
8039   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8040   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8041   for (p = pStart; p < pEnd; ++p) {
8042     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p-pStart]));
8043     if (numbers[p-pStart] < 0) numbers[p-pStart] -= shift;
8044     else                       numbers[p-pStart] += shift;
8045   }
8046   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject) dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8047   if (globalSize) {
8048     PetscLayout layout;
8049     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject) dm), globalSection, &layout));
8050     PetscCall(PetscLayoutGetSize(layout, globalSize));
8051     PetscCall(PetscLayoutDestroy(&layout));
8052   }
8053   PetscCall(PetscSectionDestroy(&section));
8054   PetscCall(PetscSectionDestroy(&globalSection));
8055   PetscFunctionReturn(0);
8056 }
8057 
8058 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8059 {
8060   PetscInt       cellHeight, cStart, cEnd;
8061 
8062   PetscFunctionBegin;
8063   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8064   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8065   else               PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8066   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8067   PetscFunctionReturn(0);
8068 }
8069 
8070 /*@
8071   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8072 
8073   Input Parameter:
8074 . dm   - The DMPlex object
8075 
8076   Output Parameter:
8077 . globalCellNumbers - Global cell numbers for all cells on this process
8078 
8079   Level: developer
8080 
8081 .seealso `DMPlexGetVertexNumbering()`
8082 @*/
8083 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8084 {
8085   DM_Plex       *mesh = (DM_Plex*) dm->data;
8086 
8087   PetscFunctionBegin;
8088   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8089   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8090   *globalCellNumbers = mesh->globalCellNumbers;
8091   PetscFunctionReturn(0);
8092 }
8093 
8094 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8095 {
8096   PetscInt       vStart, vEnd;
8097 
8098   PetscFunctionBegin;
8099   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8100   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8101   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8102   PetscFunctionReturn(0);
8103 }
8104 
8105 /*@
8106   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8107 
8108   Input Parameter:
8109 . dm   - The DMPlex object
8110 
8111   Output Parameter:
8112 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8113 
8114   Level: developer
8115 
8116 .seealso `DMPlexGetCellNumbering()`
8117 @*/
8118 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8119 {
8120   DM_Plex       *mesh = (DM_Plex*) dm->data;
8121 
8122   PetscFunctionBegin;
8123   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8124   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8125   *globalVertexNumbers = mesh->globalVertexNumbers;
8126   PetscFunctionReturn(0);
8127 }
8128 
8129 /*@
8130   DMPlexCreatePointNumbering - Create a global numbering for all points on this process
8131 
8132   Input Parameter:
8133 . dm   - The DMPlex object
8134 
8135   Output Parameter:
8136 . globalPointNumbers - Global numbers for all points on this process
8137 
8138   Level: developer
8139 
8140 .seealso `DMPlexGetCellNumbering()`
8141 @*/
8142 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8143 {
8144   IS             nums[4];
8145   PetscInt       depths[4], gdepths[4], starts[4];
8146   PetscInt       depth, d, shift = 0;
8147 
8148   PetscFunctionBegin;
8149   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8150   PetscCall(DMPlexGetDepth(dm, &depth));
8151   /* For unstratified meshes use dim instead of depth */
8152   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8153   for (d = 0; d <= depth; ++d) {
8154     PetscInt end;
8155 
8156     depths[d] = depth-d;
8157     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8158     if (!(starts[d]-end)) { starts[d] = depths[d] = -1; }
8159   }
8160   PetscCall(PetscSortIntWithArray(depth+1, starts, depths));
8161   PetscCall(MPIU_Allreduce(depths, gdepths, depth+1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject) dm)));
8162   for (d = 0; d <= depth; ++d) {
8163     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]);
8164   }
8165   for (d = 0; d <= depth; ++d) {
8166     PetscInt pStart, pEnd, gsize;
8167 
8168     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8169     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8170     shift += gsize;
8171   }
8172   PetscCall(ISConcatenate(PetscObjectComm((PetscObject) dm), depth+1, nums, globalPointNumbers));
8173   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8174   PetscFunctionReturn(0);
8175 }
8176 
8177 /*@
8178   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8179 
8180   Input Parameter:
8181 . dm - The DMPlex object
8182 
8183   Output Parameter:
8184 . ranks - The rank field
8185 
8186   Options Database Keys:
8187 . -dm_partition_view - Adds the rank field into the DM output from -dm_view using the same viewer
8188 
8189   Level: intermediate
8190 
8191 .seealso: `DMView()`
8192 @*/
8193 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8194 {
8195   DM             rdm;
8196   PetscFE        fe;
8197   PetscScalar   *r;
8198   PetscMPIInt    rank;
8199   DMPolytopeType ct;
8200   PetscInt       dim, cStart, cEnd, c;
8201   PetscBool      simplex;
8202 
8203   PetscFunctionBeginUser;
8204   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8205   PetscValidPointer(ranks, 2);
8206   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject) dm), &rank));
8207   PetscCall(DMClone(dm, &rdm));
8208   PetscCall(DMGetDimension(rdm, &dim));
8209   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8210   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8211   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct)+1 ? PETSC_TRUE : PETSC_FALSE;
8212   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8213   PetscCall(PetscObjectSetName((PetscObject) fe, "rank"));
8214   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject) fe));
8215   PetscCall(PetscFEDestroy(&fe));
8216   PetscCall(DMCreateDS(rdm));
8217   PetscCall(DMCreateGlobalVector(rdm, ranks));
8218   PetscCall(PetscObjectSetName((PetscObject) *ranks, "partition"));
8219   PetscCall(VecGetArray(*ranks, &r));
8220   for (c = cStart; c < cEnd; ++c) {
8221     PetscScalar *lr;
8222 
8223     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8224     if (lr) *lr = rank;
8225   }
8226   PetscCall(VecRestoreArray(*ranks, &r));
8227   PetscCall(DMDestroy(&rdm));
8228   PetscFunctionReturn(0);
8229 }
8230 
8231 /*@
8232   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8233 
8234   Input Parameters:
8235 + dm    - The DMPlex
8236 - label - The DMLabel
8237 
8238   Output Parameter:
8239 . val - The label value field
8240 
8241   Options Database Keys:
8242 . -dm_label_view - Adds the label value field into the DM output from -dm_view using the same viewer
8243 
8244   Level: intermediate
8245 
8246 .seealso: `DMView()`
8247 @*/
8248 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8249 {
8250   DM             rdm;
8251   PetscFE        fe;
8252   PetscScalar   *v;
8253   PetscInt       dim, cStart, cEnd, c;
8254 
8255   PetscFunctionBeginUser;
8256   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8257   PetscValidPointer(label, 2);
8258   PetscValidPointer(val, 3);
8259   PetscCall(DMClone(dm, &rdm));
8260   PetscCall(DMGetDimension(rdm, &dim));
8261   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject) rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8262   PetscCall(PetscObjectSetName((PetscObject) fe, "label_value"));
8263   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject) fe));
8264   PetscCall(PetscFEDestroy(&fe));
8265   PetscCall(DMCreateDS(rdm));
8266   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8267   PetscCall(DMCreateGlobalVector(rdm, val));
8268   PetscCall(PetscObjectSetName((PetscObject) *val, "label_value"));
8269   PetscCall(VecGetArray(*val, &v));
8270   for (c = cStart; c < cEnd; ++c) {
8271     PetscScalar *lv;
8272     PetscInt     cval;
8273 
8274     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8275     PetscCall(DMLabelGetValue(label, c, &cval));
8276     *lv = cval;
8277   }
8278   PetscCall(VecRestoreArray(*val, &v));
8279   PetscCall(DMDestroy(&rdm));
8280   PetscFunctionReturn(0);
8281 }
8282 
8283 /*@
8284   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8285 
8286   Input Parameter:
8287 . dm - The DMPlex object
8288 
8289   Notes:
8290   This is a useful diagnostic when creating meshes programmatically.
8291 
8292   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8293 
8294   Level: developer
8295 
8296 .seealso: `DMCreate()`, `DMSetFromOptions()`
8297 @*/
8298 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8299 {
8300   PetscSection    coneSection, supportSection;
8301   const PetscInt *cone, *support;
8302   PetscInt        coneSize, c, supportSize, s;
8303   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8304   PetscBool       storagecheck = PETSC_TRUE;
8305 
8306   PetscFunctionBegin;
8307   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8308   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8309   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8310   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8311   /* Check that point p is found in the support of its cone points, and vice versa */
8312   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8313   for (p = pStart; p < pEnd; ++p) {
8314     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8315     PetscCall(DMPlexGetCone(dm, p, &cone));
8316     for (c = 0; c < coneSize; ++c) {
8317       PetscBool dup = PETSC_FALSE;
8318       PetscInt  d;
8319       for (d = c-1; d >= 0; --d) {
8320         if (cone[c] == cone[d]) {dup = PETSC_TRUE; break;}
8321       }
8322       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8323       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8324       for (s = 0; s < supportSize; ++s) {
8325         if (support[s] == p) break;
8326       }
8327       if ((s >= supportSize) || (dup && (support[s+1] != p))) {
8328         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8329         for (s = 0; s < coneSize; ++s) {
8330           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8331         }
8332         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8333         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8334         for (s = 0; s < supportSize; ++s) {
8335           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8336         }
8337         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8338         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]);
8339         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8340       }
8341     }
8342     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8343     if (p != pp) { storagecheck = PETSC_FALSE; continue; }
8344     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8345     PetscCall(DMPlexGetSupport(dm, p, &support));
8346     for (s = 0; s < supportSize; ++s) {
8347       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8348       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8349       for (c = 0; c < coneSize; ++c) {
8350         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8351         if (cone[c] != pp) { c = 0; break; }
8352         if (cone[c] == p) break;
8353       }
8354       if (c >= coneSize) {
8355         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8356         for (c = 0; c < supportSize; ++c) {
8357           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8358         }
8359         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8360         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8361         for (c = 0; c < coneSize; ++c) {
8362           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8363         }
8364         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8365         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8366       }
8367     }
8368   }
8369   if (storagecheck) {
8370     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8371     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8372     PetscCheck(csize == ssize,PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8373   }
8374   PetscFunctionReturn(0);
8375 }
8376 
8377 /*
8378   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.
8379 */
8380 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
8381 {
8382   DMPolytopeType  cct;
8383   PetscInt        ptpoints[4];
8384   const PetscInt *cone, *ccone, *ptcone;
8385   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8386 
8387   PetscFunctionBegin;
8388   *unsplit = 0;
8389   switch (ct) {
8390     case DM_POLYTOPE_POINT_PRISM_TENSOR:
8391       ptpoints[npt++] = c;
8392       break;
8393     case DM_POLYTOPE_SEG_PRISM_TENSOR:
8394       PetscCall(DMPlexGetCone(dm, c, &cone));
8395       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8396       for (cp = 0; cp < coneSize; ++cp) {
8397         PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8398         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8399       }
8400       break;
8401     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8402     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8403       PetscCall(DMPlexGetCone(dm, c, &cone));
8404       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8405       for (cp = 0; cp < coneSize; ++cp) {
8406         PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8407         PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8408         for (ccp = 0; ccp < cconeSize; ++ccp) {
8409           PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8410           if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8411             PetscInt p;
8412             for (p = 0; p < npt; ++p) if (ptpoints[p] == ccone[ccp]) break;
8413             if (p == npt) ptpoints[npt++] = ccone[ccp];
8414           }
8415         }
8416       }
8417       break;
8418     default: break;
8419   }
8420   for (pt = 0; pt < npt; ++pt) {
8421     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8422     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8423   }
8424   PetscFunctionReturn(0);
8425 }
8426 
8427 /*@
8428   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8429 
8430   Input Parameters:
8431 + dm - The DMPlex object
8432 - cellHeight - Normally 0
8433 
8434   Notes:
8435   This is a useful diagnostic when creating meshes programmatically.
8436   Currently applicable only to homogeneous simplex or tensor meshes.
8437 
8438   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8439 
8440   Level: developer
8441 
8442 .seealso: `DMCreate()`, `DMSetFromOptions()`
8443 @*/
8444 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
8445 {
8446   DMPlexInterpolatedFlag interp;
8447   DMPolytopeType         ct;
8448   PetscInt               vStart, vEnd, cStart, cEnd, c;
8449 
8450   PetscFunctionBegin;
8451   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8452   PetscCall(DMPlexIsInterpolated(dm, &interp));
8453   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8454   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8455   for (c = cStart; c < cEnd; ++c) {
8456     PetscInt *closure = NULL;
8457     PetscInt  coneSize, closureSize, cl, Nv = 0;
8458 
8459     PetscCall(DMPlexGetCellType(dm, c, &ct));
8460     PetscCheck((PetscInt) ct >= 0,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
8461     if (ct == DM_POLYTOPE_UNKNOWN) continue;
8462     if (interp == DMPLEX_INTERPOLATED_FULL) {
8463       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8464       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));
8465     }
8466     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8467     for (cl = 0; cl < closureSize*2; cl += 2) {
8468       const PetscInt p = closure[cl];
8469       if ((p >= vStart) && (p < vEnd)) ++Nv;
8470     }
8471     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8472     /* Special Case: Tensor faces with identified vertices */
8473     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
8474       PetscInt unsplit;
8475 
8476       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8477       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
8478     }
8479     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));
8480   }
8481   PetscFunctionReturn(0);
8482 }
8483 
8484 /*@
8485   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
8486 
8487   Collective
8488 
8489   Input Parameters:
8490 + dm - The DMPlex object
8491 - cellHeight - Normally 0
8492 
8493   Notes:
8494   This is a useful diagnostic when creating meshes programmatically.
8495   This routine is only relevant for meshes that are fully interpolated across all ranks.
8496   It will error out if a partially interpolated mesh is given on some rank.
8497   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
8498 
8499   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8500 
8501   Level: developer
8502 
8503 .seealso: `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
8504 @*/
8505 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
8506 {
8507   PetscInt       dim, depth, vStart, vEnd, cStart, cEnd, c, h;
8508   DMPlexInterpolatedFlag interpEnum;
8509 
8510   PetscFunctionBegin;
8511   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8512   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
8513   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(0);
8514   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
8515     PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported");
8516     PetscFunctionReturn(0);
8517   }
8518 
8519   PetscCall(DMGetDimension(dm, &dim));
8520   PetscCall(DMPlexGetDepth(dm, &depth));
8521   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8522   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
8523     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
8524     for (c = cStart; c < cEnd; ++c) {
8525       const PetscInt      *cone, *ornt, *faceSizes, *faces;
8526       const DMPolytopeType *faceTypes;
8527       DMPolytopeType        ct;
8528       PetscInt              numFaces, coneSize, f;
8529       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
8530 
8531       PetscCall(DMPlexGetCellType(dm, c, &ct));
8532       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8533       if (unsplit) continue;
8534       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8535       PetscCall(DMPlexGetCone(dm, c, &cone));
8536       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
8537       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8538       for (cl = 0; cl < closureSize*2; cl += 2) {
8539         const PetscInt p = closure[cl];
8540         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
8541       }
8542       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8543       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);
8544       for (f = 0; f < numFaces; ++f) {
8545         DMPolytopeType fct;
8546         PetscInt       *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
8547 
8548         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
8549         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
8550         for (cl = 0; cl < fclosureSize*2; cl += 2) {
8551           const PetscInt p = fclosure[cl];
8552           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
8553         }
8554         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]);
8555         for (v = 0; v < fnumCorners; ++v) {
8556           if (fclosure[v] != faces[fOff+v]) {
8557             PetscInt v1;
8558 
8559             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
8560             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
8561             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
8562             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff+v1]));
8563             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8564             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]);
8565           }
8566         }
8567         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
8568         fOff += faceSizes[f];
8569       }
8570       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8571       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8572     }
8573   }
8574   PetscFunctionReturn(0);
8575 }
8576 
8577 /*@
8578   DMPlexCheckGeometry - Check the geometry of mesh cells
8579 
8580   Input Parameter:
8581 . dm - The DMPlex object
8582 
8583   Notes:
8584   This is a useful diagnostic when creating meshes programmatically.
8585 
8586   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8587 
8588   Level: developer
8589 
8590 .seealso: `DMCreate()`, `DMSetFromOptions()`
8591 @*/
8592 PetscErrorCode DMPlexCheckGeometry(DM dm)
8593 {
8594   Vec       coordinates;
8595   PetscReal detJ, J[9], refVol = 1.0;
8596   PetscReal vol;
8597   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
8598 
8599   PetscFunctionBegin;
8600   PetscCall(DMGetDimension(dm, &dim));
8601   PetscCall(DMGetCoordinateDim(dm, &dE));
8602   if (dim != dE) PetscFunctionReturn(0);
8603   PetscCall(DMPlexGetDepth(dm, &depth));
8604   for (d = 0; d < dim; ++d) refVol *= 2.0;
8605   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
8606   /* Make sure local coordinates are created, because that step is collective */
8607   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
8608   for (c = cStart; c < cEnd; ++c) {
8609     DMPolytopeType ct;
8610     PetscInt       unsplit;
8611     PetscBool      ignoreZeroVol = PETSC_FALSE;
8612 
8613     PetscCall(DMPlexGetCellType(dm, c, &ct));
8614     switch (ct) {
8615       case DM_POLYTOPE_SEG_PRISM_TENSOR:
8616       case DM_POLYTOPE_TRI_PRISM_TENSOR:
8617       case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8618         ignoreZeroVol = PETSC_TRUE; break;
8619       default: break;
8620     }
8621     switch (ct) {
8622       case DM_POLYTOPE_TRI_PRISM:
8623       case DM_POLYTOPE_TRI_PRISM_TENSOR:
8624       case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8625       case DM_POLYTOPE_PYRAMID:
8626         continue;
8627       default: break;
8628     }
8629     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8630     if (unsplit) continue;
8631     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
8632     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);
8633     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ*refVol)));
8634     /* This should work with periodicity since DG coordinates should be used */
8635     if (depth > 1) {
8636       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
8637       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);
8638       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double) vol));
8639     }
8640   }
8641   PetscFunctionReturn(0);
8642 }
8643 
8644 /*@
8645   DMPlexCheckPointSF - Check that several necessary conditions are met for the Point SF of this plex.
8646 
8647   Collective
8648 
8649   Input Parameters:
8650 + dm - The DMPlex object
8651 - pointSF - The Point SF, or NULL for Point SF attached to DM
8652 
8653   Notes:
8654   This is mainly intended for debugging/testing purposes.
8655 
8656   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8657 
8658   Level: developer
8659 
8660 .seealso: `DMGetPointSF()`, `DMSetFromOptions()`
8661 @*/
8662 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF)
8663 {
8664   PetscInt        l, nleaves, nroots, overlap;
8665   const PetscInt *locals;
8666   const PetscSFNode *remotes;
8667   PetscBool       distributed;
8668   MPI_Comm        comm;
8669   PetscMPIInt     rank;
8670 
8671   PetscFunctionBegin;
8672   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8673   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
8674   else         pointSF = dm->sf;
8675   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
8676   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
8677   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8678   {
8679     PetscMPIInt    mpiFlag;
8680 
8681     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF),&mpiFlag));
8682     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)",mpiFlag);
8683   }
8684   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
8685   PetscCall(DMPlexIsDistributed(dm, &distributed));
8686   if (!distributed) {
8687     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);
8688     PetscFunctionReturn(0);
8689   }
8690   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);
8691   PetscCall(DMPlexGetOverlap(dm, &overlap));
8692 
8693   /* Check SF graph is compatible with DMPlex chart */
8694   {
8695     PetscInt pStart, pEnd, maxLeaf;
8696 
8697     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8698     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
8699     PetscCheck(pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd-pStart, nroots);
8700     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
8701   }
8702 
8703   /* Check Point SF has no local points referenced */
8704   for (l = 0; l < nleaves; l++) {
8705     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);
8706   }
8707 
8708   /* Check there are no cells in interface */
8709   if (!overlap) {
8710     PetscInt cellHeight, cStart, cEnd;
8711 
8712     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8713     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8714     for (l = 0; l < nleaves; ++l) {
8715       const PetscInt point = locals ? locals[l] : l;
8716 
8717       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
8718     }
8719   }
8720 
8721   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
8722   {
8723     const PetscInt *rootdegree;
8724 
8725     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
8726     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
8727     for (l = 0; l < nleaves; ++l) {
8728       const PetscInt  point = locals ? locals[l] : l;
8729       const PetscInt *cone;
8730       PetscInt        coneSize, c, idx;
8731 
8732       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
8733       PetscCall(DMPlexGetCone(dm, point, &cone));
8734       for (c = 0; c < coneSize; ++c) {
8735         if (!rootdegree[cone[c]]) {
8736           if (locals) {
8737             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
8738           } else {
8739             idx = (cone[c] < nleaves) ? cone[c] : -1;
8740           }
8741           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
8742         }
8743       }
8744     }
8745   }
8746   PetscFunctionReturn(0);
8747 }
8748 
8749 /*@
8750   DMPlexCheck - Perform various checks of Plex sanity
8751 
8752   Input Parameter:
8753 . dm - The DMPlex object
8754 
8755   Notes:
8756   This is a useful diagnostic when creating meshes programmatically.
8757 
8758   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8759 
8760   Currently does not include DMPlexCheckCellShape().
8761 
8762   Level: developer
8763 
8764 .seealso: DMCreate(), DMSetFromOptions()
8765 @*/
8766 PetscErrorCode DMPlexCheck(DM dm)
8767 {
8768   PetscInt cellHeight;
8769 
8770   PetscFunctionBegin;
8771   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8772   PetscCall(DMPlexCheckSymmetry(dm));
8773   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
8774   PetscCall(DMPlexCheckFaces(dm, cellHeight));
8775   PetscCall(DMPlexCheckGeometry(dm));
8776   PetscCall(DMPlexCheckPointSF(dm, NULL));
8777   PetscCall(DMPlexCheckInterfaceCones(dm));
8778   PetscFunctionReturn(0);
8779 }
8780 
8781 typedef struct cell_stats
8782 {
8783   PetscReal min, max, sum, squaresum;
8784   PetscInt  count;
8785 } cell_stats_t;
8786 
8787 static void MPIAPI cell_stats_reduce(void *a, void *b, int * len, MPI_Datatype *datatype)
8788 {
8789   PetscInt i, N = *len;
8790 
8791   for (i = 0; i < N; i++) {
8792     cell_stats_t *A = (cell_stats_t *) a;
8793     cell_stats_t *B = (cell_stats_t *) b;
8794 
8795     B->min = PetscMin(A->min,B->min);
8796     B->max = PetscMax(A->max,B->max);
8797     B->sum += A->sum;
8798     B->squaresum += A->squaresum;
8799     B->count += A->count;
8800   }
8801 }
8802 
8803 /*@
8804   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
8805 
8806   Collective on dm
8807 
8808   Input Parameters:
8809 + dm        - The DMPlex object
8810 . output    - If true, statistics will be displayed on stdout
8811 - condLimit - Display all cells above this condition number, or PETSC_DETERMINE for no cell output
8812 
8813   Notes:
8814   This is mainly intended for debugging/testing purposes.
8815 
8816   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8817 
8818   Level: developer
8819 
8820 .seealso: `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
8821 @*/
8822 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
8823 {
8824   DM             dmCoarse;
8825   cell_stats_t   stats, globalStats;
8826   MPI_Comm       comm = PetscObjectComm((PetscObject)dm);
8827   PetscReal      *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
8828   PetscReal      limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
8829   PetscInt       cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
8830   PetscMPIInt    rank,size;
8831 
8832   PetscFunctionBegin;
8833   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8834   stats.min   = PETSC_MAX_REAL;
8835   stats.max   = PETSC_MIN_REAL;
8836   stats.sum   = stats.squaresum = 0.;
8837   stats.count = 0;
8838 
8839   PetscCallMPI(MPI_Comm_size(comm, &size));
8840   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8841   PetscCall(DMGetCoordinateDim(dm,&cdim));
8842   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
8843   PetscCall(DMPlexGetSimplexOrBoxCells(dm,0,&cStart,&cEnd));
8844   PetscCall(DMPlexGetDepthStratum(dm,1,&eStart,&eEnd));
8845   for (c = cStart; c < cEnd; c++) {
8846     PetscInt  i;
8847     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
8848 
8849     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm,c,NULL,J,invJ,&detJ));
8850     PetscCheck(detJ >= 0.0,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
8851     for (i = 0; i < PetscSqr(cdim); ++i) {
8852       frobJ    += J[i] * J[i];
8853       frobInvJ += invJ[i] * invJ[i];
8854     }
8855     cond2 = frobJ * frobInvJ;
8856     cond  = PetscSqrtReal(cond2);
8857 
8858     stats.min        = PetscMin(stats.min,cond);
8859     stats.max        = PetscMax(stats.max,cond);
8860     stats.sum       += cond;
8861     stats.squaresum += cond2;
8862     stats.count++;
8863     if (output && cond > limit) {
8864       PetscSection coordSection;
8865       Vec          coordsLocal;
8866       PetscScalar *coords = NULL;
8867       PetscInt     Nv, d, clSize, cl, *closure = NULL;
8868 
8869       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
8870       PetscCall(DMGetCoordinateSection(dm, &coordSection));
8871       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
8872       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double) cond));
8873       for (i = 0; i < Nv/cdim; ++i) {
8874         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
8875         for (d = 0; d < cdim; ++d) {
8876           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
8877           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double) PetscRealPart(coords[i*cdim+d])));
8878         }
8879         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
8880       }
8881       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
8882       for (cl = 0; cl < clSize*2; cl += 2) {
8883         const PetscInt edge = closure[cl];
8884 
8885         if ((edge >= eStart) && (edge < eEnd)) {
8886           PetscReal len;
8887 
8888           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
8889           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double) len));
8890         }
8891       }
8892       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
8893       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
8894     }
8895   }
8896   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
8897 
8898   if (size > 1) {
8899     PetscMPIInt   blockLengths[2] = {4,1};
8900     MPI_Aint      blockOffsets[2] = {offsetof(cell_stats_t,min),offsetof(cell_stats_t,count)};
8901     MPI_Datatype  blockTypes[2]   = {MPIU_REAL,MPIU_INT}, statType;
8902     MPI_Op        statReduce;
8903 
8904     PetscCallMPI(MPI_Type_create_struct(2,blockLengths,blockOffsets,blockTypes,&statType));
8905     PetscCallMPI(MPI_Type_commit(&statType));
8906     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
8907     PetscCallMPI(MPI_Reduce(&stats,&globalStats,1,statType,statReduce,0,comm));
8908     PetscCallMPI(MPI_Op_free(&statReduce));
8909     PetscCallMPI(MPI_Type_free(&statType));
8910   } else {
8911     PetscCall(PetscArraycpy(&globalStats,&stats,1));
8912   }
8913   if (rank == 0) {
8914     count = globalStats.count;
8915     min   = globalStats.min;
8916     max   = globalStats.max;
8917     mean  = globalStats.sum / globalStats.count;
8918     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1),0)) : 0.0;
8919   }
8920 
8921   if (output) {
8922     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));
8923   }
8924   PetscCall(PetscFree2(J,invJ));
8925 
8926   PetscCall(DMGetCoarseDM(dm,&dmCoarse));
8927   if (dmCoarse) {
8928     PetscBool isplex;
8929 
8930     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse,DMPLEX,&isplex));
8931     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse,output,condLimit));
8932   }
8933   PetscFunctionReturn(0);
8934 }
8935 
8936 /*@
8937   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
8938   orthogonal quality below given tolerance.
8939 
8940   Collective on dm
8941 
8942   Input Parameters:
8943 + dm   - The DMPlex object
8944 . fv   - Optional PetscFV object for pre-computed cell/face centroid information
8945 - atol - [0, 1] Absolute tolerance for tagging cells.
8946 
8947   Output Parameters:
8948 + OrthQual      - Vec containing orthogonal quality per cell
8949 - OrthQualLabel - DMLabel tagging cells below atol with DM_ADAPT_REFINE
8950 
8951   Options Database Keys:
8952 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only PETSCVIEWERASCII is
8953 supported.
8954 - -dm_plex_orthogonal_quality_vec_view - view OrthQual vector.
8955 
8956   Notes:
8957   Orthogonal quality is given by the following formula:
8958 
8959   \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]
8960 
8961   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
8962   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
8963   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
8964   calculating the cosine of the angle between these vectors.
8965 
8966   Orthogonal quality ranges from 1 (best) to 0 (worst).
8967 
8968   This routine is mainly useful for FVM, however is not restricted to only FVM. The PetscFV object is optionally used to check for
8969   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
8970 
8971   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
8972 
8973   Level: intermediate
8974 
8975 .seealso: `DMPlexCheckCellShape()`, `DMCreateLabel()`
8976 @*/
8977 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
8978 {
8979   PetscInt                nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
8980   PetscInt                *idx;
8981   PetscScalar             *oqVals;
8982   const PetscScalar       *cellGeomArr, *faceGeomArr;
8983   PetscReal               *ci, *fi, *Ai;
8984   MPI_Comm                comm;
8985   Vec                     cellgeom, facegeom;
8986   DM                      dmFace, dmCell;
8987   IS                      glob;
8988   ISLocalToGlobalMapping  ltog;
8989   PetscViewer             vwr;
8990 
8991   PetscFunctionBegin;
8992   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8993   if (fv) {PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);}
8994   PetscValidPointer(OrthQual, 4);
8995   PetscCheck(atol >= 0.0 && atol <= 1.0,PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Absolute tolerance %g not in [0,1]",(double)atol);
8996   PetscCall(PetscObjectGetComm((PetscObject) dm, &comm));
8997   PetscCall(DMGetDimension(dm, &nc));
8998   PetscCheck(nc >= 2,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
8999   {
9000     DMPlexInterpolatedFlag interpFlag;
9001 
9002     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9003     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9004       PetscMPIInt rank;
9005 
9006       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9007       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9008     }
9009   }
9010   if (OrthQualLabel) {
9011     PetscValidPointer(OrthQualLabel, 5);
9012     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9013     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9014   } else {*OrthQualLabel = NULL;}
9015   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9016   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9017   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9018   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9019   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9020   PetscCall(VecCreate(comm, OrthQual));
9021   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9022   PetscCall(VecSetSizes(*OrthQual, cEnd-cStart, PETSC_DETERMINE));
9023   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9024   PetscCall(VecSetUp(*OrthQual));
9025   PetscCall(ISDestroy(&glob));
9026   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9027   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9028   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9029   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9030   PetscCall(VecGetDM(cellgeom, &dmCell));
9031   PetscCall(VecGetDM(facegeom, &dmFace));
9032   PetscCall(PetscMalloc5(cEnd-cStart, &idx, cEnd-cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9033   for (cell = cStart; cell < cEnd; cellIter++,cell++) {
9034     PetscInt           cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9035     PetscInt           cellarr[2], *adj = NULL;
9036     PetscScalar        *cArr, *fArr;
9037     PetscReal          minvalc = 1.0, minvalf = 1.0;
9038     PetscFVCellGeom    *cg;
9039 
9040     idx[cellIter] = cell-cStart;
9041     cellarr[0] = cell;
9042     /* Make indexing into cellGeom easier */
9043     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9044     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9045     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9046     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9047     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++,cellneigh++) {
9048       PetscInt         i;
9049       const PetscInt   neigh = adj[cellneigh];
9050       PetscReal        normci = 0, normfi = 0, normai = 0;
9051       PetscFVCellGeom  *cgneigh;
9052       PetscFVFaceGeom  *fg;
9053 
9054       /* Don't count ourselves in the neighbor list */
9055       if (neigh == cell) continue;
9056       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9057       cellarr[1] = neigh;
9058       {
9059         PetscInt       numcovpts;
9060         const PetscInt *covpts;
9061 
9062         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9063         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9064         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9065       }
9066 
9067       /* Compute c_i, f_i and their norms */
9068       for (i = 0; i < nc; i++) {
9069         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9070         fi[i] = fg->centroid[i] - cg->centroid[i];
9071         Ai[i] = fg->normal[i];
9072         normci += PetscPowReal(ci[i], 2);
9073         normfi += PetscPowReal(fi[i], 2);
9074         normai += PetscPowReal(Ai[i], 2);
9075       }
9076       normci = PetscSqrtReal(normci);
9077       normfi = PetscSqrtReal(normfi);
9078       normai = PetscSqrtReal(normai);
9079 
9080       /* Normalize and compute for each face-cell-normal pair */
9081       for (i = 0; i < nc; i++) {
9082         ci[i] = ci[i]/normci;
9083         fi[i] = fi[i]/normfi;
9084         Ai[i] = Ai[i]/normai;
9085         /* PetscAbs because I don't know if normals are guaranteed to point out */
9086         cArr[cellneighiter] += PetscAbs(Ai[i]*ci[i]);
9087         fArr[cellneighiter] += PetscAbs(Ai[i]*fi[i]);
9088       }
9089       if (PetscRealPart(cArr[cellneighiter]) < minvalc) {
9090         minvalc = PetscRealPart(cArr[cellneighiter]);
9091       }
9092       if (PetscRealPart(fArr[cellneighiter]) < minvalf) {
9093         minvalf = PetscRealPart(fArr[cellneighiter]);
9094       }
9095     }
9096     PetscCall(PetscFree(adj));
9097     PetscCall(PetscFree2(cArr, fArr));
9098     /* Defer to cell if they're equal */
9099     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9100     if (OrthQualLabel) {
9101       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9102     }
9103   }
9104   PetscCall(VecSetValuesLocal(*OrthQual, cEnd-cStart, idx, oqVals, INSERT_VALUES));
9105   PetscCall(VecAssemblyBegin(*OrthQual));
9106   PetscCall(VecAssemblyEnd(*OrthQual));
9107   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9108   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9109   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9110   if (OrthQualLabel) {
9111     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9112   }
9113   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9114   PetscCall(PetscViewerDestroy(&vwr));
9115   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9116   PetscFunctionReturn(0);
9117 }
9118 
9119 /* this is here insead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9120  * interpolator construction */
9121 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9122 {
9123   PetscSection   section, newSection, gsection;
9124   PetscSF        sf;
9125   PetscBool      hasConstraints, ghasConstraints;
9126 
9127   PetscFunctionBegin;
9128   PetscValidHeaderSpecific(dm,DM_CLASSID,1);
9129   PetscValidPointer(odm,2);
9130   PetscCall(DMGetLocalSection(dm, &section));
9131   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9132   PetscCallMPI(MPI_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject) dm)));
9133   if (!ghasConstraints) {
9134     PetscCall(PetscObjectReference((PetscObject)dm));
9135     *odm = dm;
9136     PetscFunctionReturn(0);
9137   }
9138   PetscCall(DMClone(dm, odm));
9139   PetscCall(DMCopyFields(dm, *odm));
9140   PetscCall(DMGetLocalSection(*odm, &newSection));
9141   PetscCall(DMGetPointSF(*odm, &sf));
9142   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9143   PetscCall(DMSetGlobalSection(*odm, gsection));
9144   PetscCall(PetscSectionDestroy(&gsection));
9145   PetscFunctionReturn(0);
9146 }
9147 
9148 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9149 {
9150   DM             dmco, dmfo;
9151   Mat            interpo;
9152   Vec            rscale;
9153   Vec            cglobalo, clocal;
9154   Vec            fglobal, fglobalo, flocal;
9155   PetscBool      regular;
9156 
9157   PetscFunctionBegin;
9158   PetscCall(DMGetFullDM(dmc, &dmco));
9159   PetscCall(DMGetFullDM(dmf, &dmfo));
9160   PetscCall(DMSetCoarseDM(dmfo, dmco));
9161   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9162   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9163   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9164   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9165   PetscCall(DMCreateLocalVector(dmc, &clocal));
9166   PetscCall(VecSet(cglobalo, 0.));
9167   PetscCall(VecSet(clocal, 0.));
9168   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9169   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9170   PetscCall(DMCreateLocalVector(dmf, &flocal));
9171   PetscCall(VecSet(fglobal, 0.));
9172   PetscCall(VecSet(fglobalo, 0.));
9173   PetscCall(VecSet(flocal, 0.));
9174   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9175   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9176   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9177   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9178   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9179   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9180   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9181   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9182   *shift = fglobal;
9183   PetscCall(VecDestroy(&flocal));
9184   PetscCall(VecDestroy(&fglobalo));
9185   PetscCall(VecDestroy(&clocal));
9186   PetscCall(VecDestroy(&cglobalo));
9187   PetscCall(VecDestroy(&rscale));
9188   PetscCall(MatDestroy(&interpo));
9189   PetscCall(DMDestroy(&dmfo));
9190   PetscCall(DMDestroy(&dmco));
9191   PetscFunctionReturn(0);
9192 }
9193 
9194 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9195 {
9196   PetscObject    shifto;
9197   Vec            shift;
9198 
9199   PetscFunctionBegin;
9200   if (!interp) {
9201     Vec rscale;
9202 
9203     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9204     PetscCall(VecDestroy(&rscale));
9205   } else {
9206     PetscCall(PetscObjectReference((PetscObject)interp));
9207   }
9208   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9209   if (!shifto) {
9210     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9211     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject) shift));
9212     shifto = (PetscObject) shift;
9213     PetscCall(VecDestroy(&shift));
9214   }
9215   shift = (Vec) shifto;
9216   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9217   PetscCall(VecAXPY(fineSol, 1.0, shift));
9218   PetscCall(MatDestroy(&interp));
9219   PetscFunctionReturn(0);
9220 }
9221 
9222 /* Pointwise interpolation
9223      Just code FEM for now
9224      u^f = I u^c
9225      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9226      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9227      I_{ij} = psi^f_i phi^c_j
9228 */
9229 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9230 {
9231   PetscSection   gsc, gsf;
9232   PetscInt       m, n;
9233   void          *ctx;
9234   DM             cdm;
9235   PetscBool      regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9236 
9237   PetscFunctionBegin;
9238   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9239   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9240   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9241   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9242 
9243   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9244   PetscCall(MatCreate(PetscObjectComm((PetscObject) dmCoarse), interpolation));
9245   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9246   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9247   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9248 
9249   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9250   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9251   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9252   else                                            PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9253   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9254   if (scaling) {
9255     /* Use naive scaling */
9256     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9257   }
9258   PetscFunctionReturn(0);
9259 }
9260 
9261 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9262 {
9263   VecScatter     ctx;
9264 
9265   PetscFunctionBegin;
9266   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9267   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9268   PetscCall(VecScatterDestroy(&ctx));
9269   PetscFunctionReturn(0);
9270 }
9271 
9272 static void g0_identity_private(PetscInt dim, PetscInt Nf, PetscInt NfAux,
9273                                 const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[],
9274                                 const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[],
9275                                 PetscReal t, PetscReal u_tShift, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar g0[])
9276 {
9277   const PetscInt Nc = uOff[1] - uOff[0];
9278   PetscInt       c;
9279   for (c = 0; c < Nc; ++c) g0[c*Nc+c] = 1.0;
9280 }
9281 
9282 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9283 {
9284   DM             dmc;
9285   PetscDS        ds;
9286   Vec            ones, locmass;
9287   IS             cellIS;
9288   PetscFormKey   key;
9289   PetscInt       depth;
9290 
9291   PetscFunctionBegin;
9292   PetscCall(DMClone(dm, &dmc));
9293   PetscCall(DMCopyDisc(dm, dmc));
9294   PetscCall(DMGetDS(dmc, &ds));
9295   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9296   PetscCall(DMCreateGlobalVector(dmc, mass));
9297   PetscCall(DMGetLocalVector(dmc, &ones));
9298   PetscCall(DMGetLocalVector(dmc, &locmass));
9299   PetscCall(DMPlexGetDepth(dmc, &depth));
9300   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9301   PetscCall(VecSet(locmass, 0.0));
9302   PetscCall(VecSet(ones, 1.0));
9303   key.label = NULL;
9304   key.value = 0;
9305   key.field = 0;
9306   key.part  = 0;
9307   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9308   PetscCall(ISDestroy(&cellIS));
9309   PetscCall(VecSet(*mass, 0.0));
9310   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9311   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9312   PetscCall(DMRestoreLocalVector(dmc, &ones));
9313   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9314   PetscCall(DMDestroy(&dmc));
9315   PetscFunctionReturn(0);
9316 }
9317 
9318 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9319 {
9320   PetscSection   gsc, gsf;
9321   PetscInt       m, n;
9322   void          *ctx;
9323   DM             cdm;
9324   PetscBool      regular;
9325 
9326   PetscFunctionBegin;
9327   if (dmFine == dmCoarse) {
9328     DM            dmc;
9329     PetscDS       ds;
9330     PetscWeakForm wf;
9331     Vec           u;
9332     IS            cellIS;
9333     PetscFormKey  key;
9334     PetscInt      depth;
9335 
9336     PetscCall(DMClone(dmFine, &dmc));
9337     PetscCall(DMCopyDisc(dmFine, dmc));
9338     PetscCall(DMGetDS(dmc, &ds));
9339     PetscCall(PetscDSGetWeakForm(ds, &wf));
9340     PetscCall(PetscWeakFormClear(wf));
9341     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9342     PetscCall(DMCreateMatrix(dmc, mass));
9343     PetscCall(DMGetGlobalVector(dmc, &u));
9344     PetscCall(DMPlexGetDepth(dmc, &depth));
9345     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9346     PetscCall(MatZeroEntries(*mass));
9347     key.label = NULL;
9348     key.value = 0;
9349     key.field = 0;
9350     key.part  = 0;
9351     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9352     PetscCall(ISDestroy(&cellIS));
9353     PetscCall(DMRestoreGlobalVector(dmc, &u));
9354     PetscCall(DMDestroy(&dmc));
9355   } else {
9356     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9357     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9358     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9359     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9360 
9361     PetscCall(MatCreate(PetscObjectComm((PetscObject) dmCoarse), mass));
9362     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9363     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9364     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9365 
9366     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9367     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9368     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9369     else                            PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9370   }
9371   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9372   PetscFunctionReturn(0);
9373 }
9374 
9375 /*@
9376   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9377 
9378   Input Parameter:
9379 . dm - The DMPlex object
9380 
9381   Output Parameter:
9382 . regular - The flag
9383 
9384   Level: intermediate
9385 
9386 .seealso: `DMPlexSetRegularRefinement()`
9387 @*/
9388 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
9389 {
9390   PetscFunctionBegin;
9391   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9392   PetscValidBoolPointer(regular, 2);
9393   *regular = ((DM_Plex *) dm->data)->regularRefinement;
9394   PetscFunctionReturn(0);
9395 }
9396 
9397 /*@
9398   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9399 
9400   Input Parameters:
9401 + dm - The DMPlex object
9402 - regular - The flag
9403 
9404   Level: intermediate
9405 
9406 .seealso: `DMPlexGetRegularRefinement()`
9407 @*/
9408 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
9409 {
9410   PetscFunctionBegin;
9411   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9412   ((DM_Plex *) dm->data)->regularRefinement = regular;
9413   PetscFunctionReturn(0);
9414 }
9415 
9416 /* anchors */
9417 /*@
9418   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9419   call DMPlexGetAnchors() directly: if there are anchors, then DMPlexGetAnchors() is called during DMGetDefaultConstraints().
9420 
9421   not collective
9422 
9423   Input Parameter:
9424 . dm - The DMPlex object
9425 
9426   Output Parameters:
9427 + anchorSection - If not NULL, set to the section describing which points anchor the constrained points.
9428 - anchorIS - If not NULL, set to the list of anchors indexed by anchorSection
9429 
9430   Level: intermediate
9431 
9432 .seealso: `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9433 @*/
9434 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
9435 {
9436   DM_Plex *plex = (DM_Plex *)dm->data;
9437 
9438   PetscFunctionBegin;
9439   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9440   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9441   if (anchorSection) *anchorSection = plex->anchorSection;
9442   if (anchorIS) *anchorIS = plex->anchorIS;
9443   PetscFunctionReturn(0);
9444 }
9445 
9446 /*@
9447   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.  Unlike boundary conditions,
9448   when a point's degrees of freedom in a section are constrained to an outside value, the anchor constraints set a
9449   point's degrees of freedom to be a linear combination of other points' degrees of freedom.
9450 
9451   After specifying the layout of constraints with DMPlexSetAnchors(), one specifies the constraints by calling
9452   DMGetDefaultConstraints() and filling in the entries in the constraint matrix.
9453 
9454   collective on dm
9455 
9456   Input Parameters:
9457 + dm - The DMPlex object
9458 . 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).
9459 - anchorIS - The list of all anchor points.  Must have a local communicator (PETSC_COMM_SELF or derivative).
9460 
9461   The reference counts of anchorSection and anchorIS are incremented.
9462 
9463   Level: intermediate
9464 
9465 .seealso: `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9466 @*/
9467 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
9468 {
9469   DM_Plex        *plex = (DM_Plex *)dm->data;
9470   PetscMPIInt    result;
9471 
9472   PetscFunctionBegin;
9473   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9474   if (anchorSection) {
9475     PetscValidHeaderSpecific(anchorSection,PETSC_SECTION_CLASSID,2);
9476     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF,PetscObjectComm((PetscObject)anchorSection),&result));
9477     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT,PETSC_COMM_SELF,PETSC_ERR_ARG_NOTSAMECOMM,"anchor section must have local communicator");
9478   }
9479   if (anchorIS) {
9480     PetscValidHeaderSpecific(anchorIS,IS_CLASSID,3);
9481     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF,PetscObjectComm((PetscObject)anchorIS),&result));
9482     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT,PETSC_COMM_SELF,PETSC_ERR_ARG_NOTSAMECOMM,"anchor IS must have local communicator");
9483   }
9484 
9485   PetscCall(PetscObjectReference((PetscObject)anchorSection));
9486   PetscCall(PetscSectionDestroy(&plex->anchorSection));
9487   plex->anchorSection = anchorSection;
9488 
9489   PetscCall(PetscObjectReference((PetscObject)anchorIS));
9490   PetscCall(ISDestroy(&plex->anchorIS));
9491   plex->anchorIS = anchorIS;
9492 
9493   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
9494     PetscInt size, a, pStart, pEnd;
9495     const PetscInt *anchors;
9496 
9497     PetscCall(PetscSectionGetChart(anchorSection,&pStart,&pEnd));
9498     PetscCall(ISGetLocalSize(anchorIS,&size));
9499     PetscCall(ISGetIndices(anchorIS,&anchors));
9500     for (a = 0; a < size; a++) {
9501       PetscInt p;
9502 
9503       p = anchors[a];
9504       if (p >= pStart && p < pEnd) {
9505         PetscInt dof;
9506 
9507         PetscCall(PetscSectionGetDof(anchorSection,p,&dof));
9508         if (dof) {
9509 
9510           PetscCall(ISRestoreIndices(anchorIS,&anchors));
9511           SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Point %" PetscInt_FMT " cannot be constrained and an anchor",p);
9512         }
9513       }
9514     }
9515     PetscCall(ISRestoreIndices(anchorIS,&anchors));
9516   }
9517   /* reset the generic constraints */
9518   PetscCall(DMSetDefaultConstraints(dm,NULL,NULL,NULL));
9519   PetscFunctionReturn(0);
9520 }
9521 
9522 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
9523 {
9524   PetscSection anchorSection;
9525   PetscInt pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
9526 
9527   PetscFunctionBegin;
9528   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9529   PetscCall(DMPlexGetAnchors(dm,&anchorSection,NULL));
9530   PetscCall(PetscSectionCreate(PETSC_COMM_SELF,cSec));
9531   PetscCall(PetscSectionGetNumFields(section,&numFields));
9532   if (numFields) {
9533     PetscInt f;
9534     PetscCall(PetscSectionSetNumFields(*cSec,numFields));
9535 
9536     for (f = 0; f < numFields; f++) {
9537       PetscInt numComp;
9538 
9539       PetscCall(PetscSectionGetFieldComponents(section,f,&numComp));
9540       PetscCall(PetscSectionSetFieldComponents(*cSec,f,numComp));
9541     }
9542   }
9543   PetscCall(PetscSectionGetChart(anchorSection,&pStart,&pEnd));
9544   PetscCall(PetscSectionGetChart(section,&sStart,&sEnd));
9545   pStart = PetscMax(pStart,sStart);
9546   pEnd   = PetscMin(pEnd,sEnd);
9547   pEnd   = PetscMax(pStart,pEnd);
9548   PetscCall(PetscSectionSetChart(*cSec,pStart,pEnd));
9549   for (p = pStart; p < pEnd; p++) {
9550     PetscCall(PetscSectionGetDof(anchorSection,p,&dof));
9551     if (dof) {
9552       PetscCall(PetscSectionGetDof(section,p,&dof));
9553       PetscCall(PetscSectionSetDof(*cSec,p,dof));
9554       for (f = 0; f < numFields; f++) {
9555         PetscCall(PetscSectionGetFieldDof(section,p,f,&dof));
9556         PetscCall(PetscSectionSetFieldDof(*cSec,p,f,dof));
9557       }
9558     }
9559   }
9560   PetscCall(PetscSectionSetUp(*cSec));
9561   PetscCall(PetscObjectSetName((PetscObject) *cSec, "Constraint Section"));
9562   PetscFunctionReturn(0);
9563 }
9564 
9565 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
9566 {
9567   PetscSection   aSec;
9568   PetscInt       pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
9569   const PetscInt *anchors;
9570   PetscInt       numFields, f;
9571   IS             aIS;
9572   MatType        mtype;
9573   PetscBool      iscuda,iskokkos;
9574 
9575   PetscFunctionBegin;
9576   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9577   PetscCall(PetscSectionGetStorageSize(cSec, &m));
9578   PetscCall(PetscSectionGetStorageSize(section, &n));
9579   PetscCall(MatCreate(PETSC_COMM_SELF,cMat));
9580   PetscCall(MatSetSizes(*cMat,m,n,m,n));
9581   PetscCall(PetscStrcmp(dm->mattype,MATSEQAIJCUSPARSE,&iscuda));
9582   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype,MATMPIAIJCUSPARSE,&iscuda));
9583   PetscCall(PetscStrcmp(dm->mattype,MATSEQAIJKOKKOS,&iskokkos));
9584   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype,MATMPIAIJKOKKOS,&iskokkos));
9585   if (iscuda) mtype = MATSEQAIJCUSPARSE;
9586   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
9587   else mtype = MATSEQAIJ;
9588   PetscCall(MatSetType(*cMat,mtype));
9589   PetscCall(DMPlexGetAnchors(dm,&aSec,&aIS));
9590   PetscCall(ISGetIndices(aIS,&anchors));
9591   /* cSec will be a subset of aSec and section */
9592   PetscCall(PetscSectionGetChart(cSec,&pStart,&pEnd));
9593   PetscCall(PetscSectionGetChart(section,&sStart,&sEnd));
9594   PetscCall(PetscMalloc1(m+1,&i));
9595   i[0] = 0;
9596   PetscCall(PetscSectionGetNumFields(section,&numFields));
9597   for (p = pStart; p < pEnd; p++) {
9598     PetscInt rDof, rOff, r;
9599 
9600     PetscCall(PetscSectionGetDof(aSec,p,&rDof));
9601     if (!rDof) continue;
9602     PetscCall(PetscSectionGetOffset(aSec,p,&rOff));
9603     if (numFields) {
9604       for (f = 0; f < numFields; f++) {
9605         annz = 0;
9606         for (r = 0; r < rDof; r++) {
9607           a = anchors[rOff + r];
9608           if (a < sStart || a >= sEnd) continue;
9609           PetscCall(PetscSectionGetFieldDof(section,a,f,&aDof));
9610           annz += aDof;
9611         }
9612         PetscCall(PetscSectionGetFieldDof(cSec,p,f,&dof));
9613         PetscCall(PetscSectionGetFieldOffset(cSec,p,f,&off));
9614         for (q = 0; q < dof; q++) {
9615           i[off + q + 1] = i[off + q] + annz;
9616         }
9617       }
9618     } else {
9619       annz = 0;
9620       PetscCall(PetscSectionGetDof(cSec,p,&dof));
9621       for (q = 0; q < dof; q++) {
9622         a = anchors[rOff + q];
9623         if (a < sStart || a >= sEnd) continue;
9624         PetscCall(PetscSectionGetDof(section,a,&aDof));
9625         annz += aDof;
9626       }
9627       PetscCall(PetscSectionGetDof(cSec,p,&dof));
9628       PetscCall(PetscSectionGetOffset(cSec,p,&off));
9629       for (q = 0; q < dof; q++) {
9630         i[off + q + 1] = i[off + q] + annz;
9631       }
9632     }
9633   }
9634   nnz = i[m];
9635   PetscCall(PetscMalloc1(nnz,&j));
9636   offset = 0;
9637   for (p = pStart; p < pEnd; p++) {
9638     if (numFields) {
9639       for (f = 0; f < numFields; f++) {
9640         PetscCall(PetscSectionGetFieldDof(cSec,p,f,&dof));
9641         for (q = 0; q < dof; q++) {
9642           PetscInt rDof, rOff, r;
9643           PetscCall(PetscSectionGetDof(aSec,p,&rDof));
9644           PetscCall(PetscSectionGetOffset(aSec,p,&rOff));
9645           for (r = 0; r < rDof; r++) {
9646             PetscInt s;
9647 
9648             a = anchors[rOff + r];
9649             if (a < sStart || a >= sEnd) continue;
9650             PetscCall(PetscSectionGetFieldDof(section,a,f,&aDof));
9651             PetscCall(PetscSectionGetFieldOffset(section,a,f,&aOff));
9652             for (s = 0; s < aDof; s++) {
9653               j[offset++] = aOff + s;
9654             }
9655           }
9656         }
9657       }
9658     } else {
9659       PetscCall(PetscSectionGetDof(cSec,p,&dof));
9660       for (q = 0; q < dof; q++) {
9661         PetscInt rDof, rOff, r;
9662         PetscCall(PetscSectionGetDof(aSec,p,&rDof));
9663         PetscCall(PetscSectionGetOffset(aSec,p,&rOff));
9664         for (r = 0; r < rDof; r++) {
9665           PetscInt s;
9666 
9667           a = anchors[rOff + r];
9668           if (a < sStart || a >= sEnd) continue;
9669           PetscCall(PetscSectionGetDof(section,a,&aDof));
9670           PetscCall(PetscSectionGetOffset(section,a,&aOff));
9671           for (s = 0; s < aDof; s++) {
9672             j[offset++] = aOff + s;
9673           }
9674         }
9675       }
9676     }
9677   }
9678   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat,i,j,NULL));
9679   PetscCall(PetscFree(i));
9680   PetscCall(PetscFree(j));
9681   PetscCall(ISRestoreIndices(aIS,&anchors));
9682   PetscFunctionReturn(0);
9683 }
9684 
9685 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
9686 {
9687   DM_Plex        *plex = (DM_Plex *)dm->data;
9688   PetscSection   anchorSection, section, cSec;
9689   Mat            cMat;
9690 
9691   PetscFunctionBegin;
9692   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9693   PetscCall(DMPlexGetAnchors(dm,&anchorSection,NULL));
9694   if (anchorSection) {
9695     PetscInt Nf;
9696 
9697     PetscCall(DMGetLocalSection(dm,&section));
9698     PetscCall(DMPlexCreateConstraintSection_Anchors(dm,section,&cSec));
9699     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm,section,cSec,&cMat));
9700     PetscCall(DMGetNumFields(dm,&Nf));
9701     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm,section,cSec,cMat));
9702     PetscCall(DMSetDefaultConstraints(dm,cSec,cMat,NULL));
9703     PetscCall(PetscSectionDestroy(&cSec));
9704     PetscCall(MatDestroy(&cMat));
9705   }
9706   PetscFunctionReturn(0);
9707 }
9708 
9709 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
9710 {
9711   IS             subis;
9712   PetscSection   section, subsection;
9713 
9714   PetscFunctionBegin;
9715   PetscCall(DMGetLocalSection(dm, &section));
9716   PetscCheck(section,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
9717   PetscCheck(subdm,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
9718   /* Create subdomain */
9719   PetscCall(DMPlexFilter(dm, label, value, subdm));
9720   /* Create submodel */
9721   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
9722   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
9723   PetscCall(DMSetLocalSection(*subdm, subsection));
9724   PetscCall(PetscSectionDestroy(&subsection));
9725   PetscCall(DMCopyDisc(dm, *subdm));
9726   /* Create map from submodel to global model */
9727   if (is) {
9728     PetscSection    sectionGlobal, subsectionGlobal;
9729     IS              spIS;
9730     const PetscInt *spmap;
9731     PetscInt       *subIndices;
9732     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
9733     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
9734 
9735     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
9736     PetscCall(ISGetIndices(spIS, &spmap));
9737     PetscCall(PetscSectionGetNumFields(section, &Nf));
9738     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
9739     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
9740     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
9741     for (p = pStart; p < pEnd; ++p) {
9742       PetscInt gdof, pSubSize  = 0;
9743 
9744       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
9745       if (gdof > 0) {
9746         for (f = 0; f < Nf; ++f) {
9747           PetscInt fdof, fcdof;
9748 
9749           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
9750           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
9751           pSubSize += fdof-fcdof;
9752         }
9753         subSize += pSubSize;
9754         if (pSubSize) {
9755           if (bs < 0) {
9756             bs = pSubSize;
9757           } else if (bs != pSubSize) {
9758             /* Layout does not admit a pointwise block size */
9759             bs = 1;
9760           }
9761         }
9762       }
9763     }
9764     /* Must have same blocksize on all procs (some might have no points) */
9765     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs; bsLocal[1] = bs;
9766     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject) dm), bsLocal, bsMinMax));
9767     if (bsMinMax[0] != bsMinMax[1]) {bs = 1;}
9768     else                            {bs = bsMinMax[0];}
9769     PetscCall(PetscMalloc1(subSize, &subIndices));
9770     for (p = pStart; p < pEnd; ++p) {
9771       PetscInt gdof, goff;
9772 
9773       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
9774       if (gdof > 0) {
9775         const PetscInt point = spmap[p];
9776 
9777         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
9778         for (f = 0; f < Nf; ++f) {
9779           PetscInt fdof, fcdof, fc, f2, poff = 0;
9780 
9781           /* Can get rid of this loop by storing field information in the global section */
9782           for (f2 = 0; f2 < f; ++f2) {
9783             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
9784             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
9785             poff += fdof-fcdof;
9786           }
9787           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
9788           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
9789           for (fc = 0; fc < fdof-fcdof; ++fc, ++subOff) {
9790             subIndices[subOff] = goff+poff+fc;
9791           }
9792         }
9793       }
9794     }
9795     PetscCall(ISRestoreIndices(spIS, &spmap));
9796     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
9797     if (bs > 1) {
9798       /* We need to check that the block size does not come from non-contiguous fields */
9799       PetscInt i, j, set = 1;
9800       for (i = 0; i < subSize; i += bs) {
9801         for (j = 0; j < bs; ++j) {
9802           if (subIndices[i+j] != subIndices[i]+j) {set = 0; break;}
9803         }
9804       }
9805       if (set) PetscCall(ISSetBlockSize(*is, bs));
9806     }
9807     /* Attach nullspace */
9808     for (f = 0; f < Nf; ++f) {
9809       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
9810       if ((*subdm)->nullspaceConstructors[f]) break;
9811     }
9812     if (f < Nf) {
9813       MatNullSpace nullSpace;
9814       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
9815 
9816       PetscCall(PetscObjectCompose((PetscObject) *is, "nullspace", (PetscObject) nullSpace));
9817       PetscCall(MatNullSpaceDestroy(&nullSpace));
9818     }
9819   }
9820   PetscFunctionReturn(0);
9821 }
9822 
9823 /*@
9824   DMPlexMonitorThroughput - Report the cell throughput of FE integration
9825 
9826   Input Parameter:
9827 - dm - The DM
9828 
9829   Level: developer
9830 
9831   Options Database Keys:
9832 . -dm_plex_monitor_throughput - Activate the monitor
9833 
9834 .seealso: `DMSetFromOptions()`, `DMPlexCreate()`
9835 @*/
9836 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
9837 {
9838 #if defined(PETSC_USE_LOG)
9839   PetscStageLog      stageLog;
9840   PetscLogEvent      event;
9841   PetscLogStage      stage;
9842   PetscEventPerfInfo eventInfo;
9843   PetscReal          cellRate, flopRate;
9844   PetscInt           cStart, cEnd, Nf, N;
9845   const char        *name;
9846 #endif
9847 
9848   PetscFunctionBegin;
9849   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9850 #if defined(PETSC_USE_LOG)
9851   PetscCall(PetscObjectGetName((PetscObject) dm, &name));
9852   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9853   PetscCall(DMGetNumFields(dm, &Nf));
9854   PetscCall(PetscLogGetStageLog(&stageLog));
9855   PetscCall(PetscStageLogGetCurrent(stageLog, &stage));
9856   PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
9857   PetscCall(PetscLogEventGetPerfInfo(stage, event, &eventInfo));
9858   N        = (cEnd - cStart)*Nf*eventInfo.count;
9859   flopRate = eventInfo.flops/eventInfo.time;
9860   cellRate = N/eventInfo.time;
9861   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)));
9862 #else
9863   SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Plex Throughput Monitor is not supported if logging is turned off. Reconfigure using --with-log.");
9864 #endif
9865   PetscFunctionReturn(0);
9866 }
9867