xref: /petsc/src/dm/impls/plex/plex.c (revision 76bd3646d776ac16fc835ea742fa097f15759704)
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 
10 /* Logging support */
11 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;
12 
13 PETSC_EXTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
14 
15 /*@
16   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
17 
18   Input Parameter:
19 + dm     - The DMPlex object
20 - height - The cell height in the Plex, 0 is the default
21 
22   Output Parameters:
23 + cStart - The first "normal" cell
24 - cEnd   - The upper bound on "normal"" cells
25 
26   Note: This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
27 
28   Level: developer
29 
30 .seealso DMPlexConstructGhostCells(), DMPlexSetGhostCellStratum()
31 @*/
32 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd)
33 {
34   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
35   PetscInt       cS, cE, c;
36   PetscErrorCode ierr;
37 
38   PetscFunctionBegin;
39   ierr = DMPlexGetHeightStratum(dm, PetscMax(height, 0), &cS, &cE);CHKERRQ(ierr);
40   for (c = cS; c < cE; ++c) {
41     DMPolytopeType cct;
42 
43     ierr = DMPlexGetCellType(dm, c, &cct);CHKERRQ(ierr);
44     if ((PetscInt) cct < 0) break;
45     switch (cct) {
46       case DM_POLYTOPE_POINT:
47       case DM_POLYTOPE_SEGMENT:
48       case DM_POLYTOPE_TRIANGLE:
49       case DM_POLYTOPE_QUADRILATERAL:
50       case DM_POLYTOPE_TETRAHEDRON:
51       case DM_POLYTOPE_HEXAHEDRON:
52         ct = cct;
53         break;
54       default: break;
55     }
56     if (ct != DM_POLYTOPE_UNKNOWN) break;
57   }
58   if (ct != DM_POLYTOPE_UNKNOWN) {
59     DMLabel ctLabel;
60 
61     ierr = DMPlexGetCellTypeLabel(dm, &ctLabel);CHKERRQ(ierr);
62     ierr = DMLabelGetStratumBounds(ctLabel, ct, &cS, &cE);CHKERRQ(ierr);
63   }
64   if (cStart) *cStart = cS;
65   if (cEnd)   *cEnd   = cE;
66   PetscFunctionReturn(0);
67 }
68 
69 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
70 {
71   PetscInt       cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
72   PetscInt       vcdof[2] = {0,0}, globalvcdof[2];
73   PetscErrorCode ierr;
74 
75   PetscFunctionBegin;
76   *ft  = PETSC_VTK_INVALID;
77   ierr = DMGetCoordinateDim(dm, &cdim);CHKERRQ(ierr);
78   ierr = DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd);CHKERRQ(ierr);
79   ierr = DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd);CHKERRQ(ierr);
80   ierr = PetscSectionGetChart(section, &pStart, &pEnd);CHKERRQ(ierr);
81   if (field >= 0) {
82     if ((vStart >= pStart) && (vStart < pEnd)) {ierr = PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]);CHKERRQ(ierr);}
83     if ((cStart >= pStart) && (cStart < pEnd)) {ierr = PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]);CHKERRQ(ierr);}
84   } else {
85     if ((vStart >= pStart) && (vStart < pEnd)) {ierr = PetscSectionGetDof(section, vStart, &vcdof[0]);CHKERRQ(ierr);}
86     if ((cStart >= pStart) && (cStart < pEnd)) {ierr = PetscSectionGetDof(section, cStart, &vcdof[1]);CHKERRQ(ierr);}
87   }
88   ierr = MPI_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm));CHKERRQ(ierr);
89   if (globalvcdof[0]) {
90     *sStart = vStart;
91     *sEnd   = vEnd;
92     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
93     else                        *ft = PETSC_VTK_POINT_FIELD;
94   } else if (globalvcdof[1]) {
95     *sStart = cStart;
96     *sEnd   = cEnd;
97     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
98     else                        *ft = PETSC_VTK_CELL_FIELD;
99   } else {
100     if (field >= 0) {
101       const char *fieldname;
102 
103       ierr = PetscSectionGetFieldName(section, field, &fieldname);CHKERRQ(ierr);
104       ierr = PetscInfo2((PetscObject) dm, "Could not classify VTK output type of section field %D \"%s\"\n", field, fieldname);CHKERRQ(ierr);
105     } else {
106       ierr = PetscInfo((PetscObject) dm, "Could not classify VTK output typp of section\"%s\"\n");CHKERRQ(ierr);
107     }
108   }
109   PetscFunctionReturn(0);
110 }
111 
112 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
113 {
114   DM                 dm;
115   PetscSection       s;
116   PetscDraw          draw, popup;
117   DM                 cdm;
118   PetscSection       coordSection;
119   Vec                coordinates;
120   const PetscScalar *coords, *array;
121   PetscReal          bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
122   PetscReal          vbound[2], time;
123   PetscBool          isnull, flg;
124   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
125   const char        *name;
126   char               title[PETSC_MAX_PATH_LEN];
127   PetscErrorCode     ierr;
128 
129   PetscFunctionBegin;
130   ierr = PetscViewerDrawGetDraw(viewer, 0, &draw);CHKERRQ(ierr);
131   ierr = PetscDrawIsNull(draw, &isnull);CHKERRQ(ierr);
132   if (isnull) PetscFunctionReturn(0);
133 
134   ierr = VecGetDM(v, &dm);CHKERRQ(ierr);
135   ierr = DMGetCoordinateDim(dm, &dim);CHKERRQ(ierr);
136   if (dim != 2) SETERRQ1(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %D. Use PETSCVIEWERGLVIS", dim);
137   ierr = DMGetLocalSection(dm, &s);CHKERRQ(ierr);
138   ierr = PetscSectionGetNumFields(s, &Nf);CHKERRQ(ierr);
139   ierr = DMGetCoarsenLevel(dm, &level);CHKERRQ(ierr);
140   ierr = DMGetCoordinateDM(dm, &cdm);CHKERRQ(ierr);
141   ierr = DMGetLocalSection(cdm, &coordSection);CHKERRQ(ierr);
142   ierr = DMGetCoordinatesLocal(dm, &coordinates);CHKERRQ(ierr);
143   ierr = DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd);CHKERRQ(ierr);
144   ierr = DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd);CHKERRQ(ierr);
145 
146   ierr = PetscObjectGetName((PetscObject) v, &name);CHKERRQ(ierr);
147   ierr = DMGetOutputSequenceNumber(dm, &step, &time);CHKERRQ(ierr);
148 
149   ierr = VecGetLocalSize(coordinates, &N);CHKERRQ(ierr);
150   ierr = VecGetArrayRead(coordinates, &coords);CHKERRQ(ierr);
151   for (c = 0; c < N; c += dim) {
152     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));   bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
153     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c+1])); bound[3] = PetscMax(bound[3], PetscRealPart(coords[c+1]));
154   }
155   ierr = VecRestoreArrayRead(coordinates, &coords);CHKERRQ(ierr);
156   ierr = PetscDrawClear(draw);CHKERRQ(ierr);
157 
158   /* Could implement something like DMDASelectFields() */
159   for (f = 0; f < Nf; ++f) {
160     DM   fdm = dm;
161     Vec  fv  = v;
162     IS   fis;
163     char prefix[PETSC_MAX_PATH_LEN];
164     const char *fname;
165 
166     ierr = PetscSectionGetFieldComponents(s, f, &Nc);CHKERRQ(ierr);
167     ierr = PetscSectionGetFieldName(s, f, &fname);CHKERRQ(ierr);
168 
169     if (v->hdr.prefix) {ierr = PetscStrncpy(prefix, v->hdr.prefix,sizeof(prefix));CHKERRQ(ierr);}
170     else               {prefix[0] = '\0';}
171     if (Nf > 1) {
172       ierr = DMCreateSubDM(dm, 1, &f, &fis, &fdm);CHKERRQ(ierr);
173       ierr = VecGetSubVector(v, fis, &fv);CHKERRQ(ierr);
174       ierr = PetscStrlcat(prefix, fname,sizeof(prefix));CHKERRQ(ierr);
175       ierr = PetscStrlcat(prefix, "_",sizeof(prefix));CHKERRQ(ierr);
176     }
177     for (comp = 0; comp < Nc; ++comp, ++w) {
178       PetscInt nmax = 2;
179 
180       ierr = PetscViewerDrawGetDraw(viewer, w, &draw);CHKERRQ(ierr);
181       if (Nc > 1) {ierr = PetscSNPrintf(title, sizeof(title), "%s:%s_%D Step: %D Time: %.4g", name, fname, comp, step, time);CHKERRQ(ierr);}
182       else        {ierr = PetscSNPrintf(title, sizeof(title), "%s:%s Step: %D Time: %.4g", name, fname, step, time);CHKERRQ(ierr);}
183       ierr = PetscDrawSetTitle(draw, title);CHKERRQ(ierr);
184 
185       /* TODO Get max and min only for this component */
186       ierr = PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg);CHKERRQ(ierr);
187       if (!flg) {
188         ierr = VecMin(fv, NULL, &vbound[0]);CHKERRQ(ierr);
189         ierr = VecMax(fv, NULL, &vbound[1]);CHKERRQ(ierr);
190         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
191       }
192       ierr = PetscDrawGetPopup(draw, &popup);CHKERRQ(ierr);
193       ierr = PetscDrawScalePopup(popup, vbound[0], vbound[1]);CHKERRQ(ierr);
194       ierr = PetscDrawSetCoordinates(draw, bound[0], bound[1], bound[2], bound[3]);CHKERRQ(ierr);
195 
196       ierr = VecGetArrayRead(fv, &array);CHKERRQ(ierr);
197       for (c = cStart; c < cEnd; ++c) {
198         PetscScalar *coords = NULL, *a = NULL;
199         PetscInt     numCoords, color[4] = {-1,-1,-1,-1};
200 
201         ierr = DMPlexPointLocalRead(fdm, c, array, &a);CHKERRQ(ierr);
202         if (a) {
203           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
204           color[1] = color[2] = color[3] = color[0];
205         } else {
206           PetscScalar *vals = NULL;
207           PetscInt     numVals, va;
208 
209           ierr = DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals);CHKERRQ(ierr);
210           if (numVals % Nc) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "The number of components %D does not divide the number of values in the closure %D", Nc, numVals);
211           switch (numVals/Nc) {
212           case 3: /* P1 Triangle */
213           case 4: /* P1 Quadrangle */
214             for (va = 0; va < numVals/Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va*Nc+comp]), vbound[0], vbound[1]);
215             break;
216           case 6: /* P2 Triangle */
217           case 8: /* P2 Quadrangle */
218             for (va = 0; va < numVals/(Nc*2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va*Nc+comp + numVals/(Nc*2)]), vbound[0], vbound[1]);
219             break;
220           default: SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %D cannot be handled", numVals/Nc);
221           }
222           ierr = DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals);CHKERRQ(ierr);
223         }
224         ierr = DMPlexVecGetClosure(dm, coordSection, coordinates, c, &numCoords, &coords);CHKERRQ(ierr);
225         switch (numCoords) {
226         case 6:
227           ierr = 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]);CHKERRQ(ierr);
228           break;
229         case 8:
230           ierr = 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]);CHKERRQ(ierr);
231           ierr = 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]);CHKERRQ(ierr);
232           break;
233         default: SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %D coordinates", numCoords);
234         }
235         ierr = DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords);CHKERRQ(ierr);
236       }
237       ierr = VecRestoreArrayRead(fv, &array);CHKERRQ(ierr);
238       ierr = PetscDrawFlush(draw);CHKERRQ(ierr);
239       ierr = PetscDrawPause(draw);CHKERRQ(ierr);
240       ierr = PetscDrawSave(draw);CHKERRQ(ierr);
241     }
242     if (Nf > 1) {
243       ierr = VecRestoreSubVector(v, fis, &fv);CHKERRQ(ierr);
244       ierr = ISDestroy(&fis);CHKERRQ(ierr);
245       ierr = DMDestroy(&fdm);CHKERRQ(ierr);
246     }
247   }
248   PetscFunctionReturn(0);
249 }
250 
251 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
252 {
253   DM                      dm;
254   Vec                     locv;
255   const char              *name;
256   PetscSection            section;
257   PetscInt                pStart, pEnd;
258   PetscInt                numFields;
259   PetscViewerVTKFieldType ft;
260   PetscErrorCode          ierr;
261 
262   PetscFunctionBegin;
263   ierr = VecGetDM(v, &dm);CHKERRQ(ierr);
264   ierr = DMCreateLocalVector(dm, &locv);CHKERRQ(ierr); /* VTK viewer requires exclusive ownership of the vector */
265   ierr = PetscObjectGetName((PetscObject) v, &name);CHKERRQ(ierr);
266   ierr = PetscObjectSetName((PetscObject) locv, name);CHKERRQ(ierr);
267   ierr = VecCopy(v, locv);CHKERRQ(ierr);
268   ierr = DMGetLocalSection(dm, &section);CHKERRQ(ierr);
269   ierr = PetscSectionGetNumFields(section, &numFields);CHKERRQ(ierr);
270   if (!numFields) {
271     ierr = DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft);CHKERRQ(ierr);
272     ierr = PetscViewerVTKAddField(viewer, (PetscObject) dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE,(PetscObject) locv);CHKERRQ(ierr);
273   } else {
274     PetscInt f;
275 
276     for (f = 0; f < numFields; f++) {
277       ierr = DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft);CHKERRQ(ierr);
278       if (ft == PETSC_VTK_INVALID) continue;
279       ierr = PetscObjectReference((PetscObject)locv);CHKERRQ(ierr);
280       ierr = PetscViewerVTKAddField(viewer, (PetscObject) dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE,(PetscObject) locv);CHKERRQ(ierr);
281     }
282     ierr = VecDestroy(&locv);CHKERRQ(ierr);
283   }
284   PetscFunctionReturn(0);
285 }
286 
287 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
288 {
289   DM             dm;
290   PetscBool      isvtk, ishdf5, isdraw, isglvis;
291   PetscErrorCode ierr;
292 
293   PetscFunctionBegin;
294   ierr = VecGetDM(v, &dm);CHKERRQ(ierr);
295   if (!dm) SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
296   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,   &isvtk);CHKERRQ(ierr);
297   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,  &ishdf5);CHKERRQ(ierr);
298   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERDRAW,  &isdraw);CHKERRQ(ierr);
299   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERGLVIS, &isglvis);CHKERRQ(ierr);
300   if (isvtk || ishdf5 || isdraw || isglvis) {
301     PetscInt    i,numFields;
302     PetscObject fe;
303     PetscBool   fem = PETSC_FALSE;
304     Vec         locv = v;
305     const char  *name;
306     PetscInt    step;
307     PetscReal   time;
308 
309     ierr = DMGetNumFields(dm, &numFields);CHKERRQ(ierr);
310     for (i=0; i<numFields; i++) {
311       ierr = DMGetField(dm, i, NULL, &fe);CHKERRQ(ierr);
312       if (fe->classid == PETSCFE_CLASSID) { fem = PETSC_TRUE; break; }
313     }
314     if (fem) {
315       ierr = DMGetLocalVector(dm, &locv);CHKERRQ(ierr);
316       ierr = PetscObjectGetName((PetscObject) v, &name);CHKERRQ(ierr);
317       ierr = PetscObjectSetName((PetscObject) locv, name);CHKERRQ(ierr);
318       ierr = VecCopy(v, locv);CHKERRQ(ierr);
319       ierr = DMGetOutputSequenceNumber(dm, NULL, &time);CHKERRQ(ierr);
320       ierr = DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL);CHKERRQ(ierr);
321     }
322     if (isvtk) {
323       ierr = VecView_Plex_Local_VTK(locv, viewer);CHKERRQ(ierr);
324     } else if (ishdf5) {
325 #if defined(PETSC_HAVE_HDF5)
326       ierr = VecView_Plex_Local_HDF5_Internal(locv, viewer);CHKERRQ(ierr);
327 #else
328       SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
329 #endif
330     } else if (isdraw) {
331       ierr = VecView_Plex_Local_Draw(locv, viewer);CHKERRQ(ierr);
332     } else if (isglvis) {
333       ierr = DMGetOutputSequenceNumber(dm, &step, NULL);CHKERRQ(ierr);
334       ierr = PetscViewerGLVisSetSnapId(viewer, step);CHKERRQ(ierr);
335       ierr = VecView_GLVis(locv, viewer);CHKERRQ(ierr);
336     }
337     if (fem) {ierr = DMRestoreLocalVector(dm, &locv);CHKERRQ(ierr);}
338   } else {
339     PetscBool isseq;
340 
341     ierr = PetscObjectTypeCompare((PetscObject) v, VECSEQ, &isseq);CHKERRQ(ierr);
342     if (isseq) {ierr = VecView_Seq(v, viewer);CHKERRQ(ierr);}
343     else       {ierr = VecView_MPI(v, viewer);CHKERRQ(ierr);}
344   }
345   PetscFunctionReturn(0);
346 }
347 
348 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
349 {
350   DM             dm;
351   PetscBool      isvtk, ishdf5, isdraw, isglvis;
352   PetscErrorCode ierr;
353 
354   PetscFunctionBegin;
355   ierr = VecGetDM(v, &dm);CHKERRQ(ierr);
356   if (!dm) SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
357   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,   &isvtk);CHKERRQ(ierr);
358   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,  &ishdf5);CHKERRQ(ierr);
359   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERDRAW,  &isdraw);CHKERRQ(ierr);
360   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERGLVIS, &isglvis);CHKERRQ(ierr);
361   if (isvtk || isdraw || isglvis) {
362     Vec         locv;
363     const char *name;
364 
365     ierr = DMGetLocalVector(dm, &locv);CHKERRQ(ierr);
366     ierr = PetscObjectGetName((PetscObject) v, &name);CHKERRQ(ierr);
367     ierr = PetscObjectSetName((PetscObject) locv, name);CHKERRQ(ierr);
368     ierr = DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv);CHKERRQ(ierr);
369     ierr = DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv);CHKERRQ(ierr);
370     ierr = VecView_Plex_Local(locv, viewer);CHKERRQ(ierr);
371     ierr = DMRestoreLocalVector(dm, &locv);CHKERRQ(ierr);
372   } else if (ishdf5) {
373 #if defined(PETSC_HAVE_HDF5)
374     ierr = VecView_Plex_HDF5_Internal(v, viewer);CHKERRQ(ierr);
375 #else
376     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
377 #endif
378   } else {
379     PetscBool isseq;
380 
381     ierr = PetscObjectTypeCompare((PetscObject) v, VECSEQ, &isseq);CHKERRQ(ierr);
382     if (isseq) {ierr = VecView_Seq(v, viewer);CHKERRQ(ierr);}
383     else       {ierr = VecView_MPI(v, viewer);CHKERRQ(ierr);}
384   }
385   PetscFunctionReturn(0);
386 }
387 
388 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
389 {
390   DM                dm;
391   MPI_Comm          comm;
392   PetscViewerFormat format;
393   Vec               v;
394   PetscBool         isvtk, ishdf5;
395   PetscErrorCode    ierr;
396 
397   PetscFunctionBegin;
398   ierr = VecGetDM(originalv, &dm);CHKERRQ(ierr);
399   ierr = PetscObjectGetComm((PetscObject) originalv, &comm);CHKERRQ(ierr);
400   if (!dm) SETERRQ(comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
401   ierr = PetscViewerGetFormat(viewer, &format);CHKERRQ(ierr);
402   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5);CHKERRQ(ierr);
403   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,  &isvtk);CHKERRQ(ierr);
404   if (format == PETSC_VIEWER_NATIVE) {
405     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
406     /* this need a better fix */
407     if (dm->useNatural) {
408       if (dm->sfNatural) {
409         const char *vecname;
410         PetscInt    n, nroots;
411 
412         ierr = VecGetLocalSize(originalv, &n);CHKERRQ(ierr);
413         ierr = PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL);CHKERRQ(ierr);
414         if (n == nroots) {
415           ierr = DMGetGlobalVector(dm, &v);CHKERRQ(ierr);
416           ierr = DMPlexGlobalToNaturalBegin(dm, originalv, v);CHKERRQ(ierr);
417           ierr = DMPlexGlobalToNaturalEnd(dm, originalv, v);CHKERRQ(ierr);
418           ierr = PetscObjectGetName((PetscObject) originalv, &vecname);CHKERRQ(ierr);
419           ierr = PetscObjectSetName((PetscObject) v, vecname);CHKERRQ(ierr);
420         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
421       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
422     } else v = originalv;
423   } else v = originalv;
424 
425   if (ishdf5) {
426 #if defined(PETSC_HAVE_HDF5)
427     ierr = VecView_Plex_HDF5_Native_Internal(v, viewer);CHKERRQ(ierr);
428 #else
429     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
430 #endif
431   } else if (isvtk) {
432     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
433   } else {
434     PetscBool isseq;
435 
436     ierr = PetscObjectTypeCompare((PetscObject) v, VECSEQ, &isseq);CHKERRQ(ierr);
437     if (isseq) {ierr = VecView_Seq(v, viewer);CHKERRQ(ierr);}
438     else       {ierr = VecView_MPI(v, viewer);CHKERRQ(ierr);}
439   }
440   if (v != originalv) {ierr = DMRestoreGlobalVector(dm, &v);CHKERRQ(ierr);}
441   PetscFunctionReturn(0);
442 }
443 
444 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
445 {
446   DM             dm;
447   PetscBool      ishdf5;
448   PetscErrorCode ierr;
449 
450   PetscFunctionBegin;
451   ierr = VecGetDM(v, &dm);CHKERRQ(ierr);
452   if (!dm) SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
453   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5);CHKERRQ(ierr);
454   if (ishdf5) {
455     DM          dmBC;
456     Vec         gv;
457     const char *name;
458 
459     ierr = DMGetOutputDM(dm, &dmBC);CHKERRQ(ierr);
460     ierr = DMGetGlobalVector(dmBC, &gv);CHKERRQ(ierr);
461     ierr = PetscObjectGetName((PetscObject) v, &name);CHKERRQ(ierr);
462     ierr = PetscObjectSetName((PetscObject) gv, name);CHKERRQ(ierr);
463     ierr = VecLoad_Default(gv, viewer);CHKERRQ(ierr);
464     ierr = DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v);CHKERRQ(ierr);
465     ierr = DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v);CHKERRQ(ierr);
466     ierr = DMRestoreGlobalVector(dmBC, &gv);CHKERRQ(ierr);
467   } else {
468     ierr = VecLoad_Default(v, viewer);CHKERRQ(ierr);
469   }
470   PetscFunctionReturn(0);
471 }
472 
473 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
474 {
475   DM             dm;
476   PetscBool      ishdf5;
477   PetscErrorCode ierr;
478 
479   PetscFunctionBegin;
480   ierr = VecGetDM(v, &dm);CHKERRQ(ierr);
481   if (!dm) SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
482   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5);CHKERRQ(ierr);
483   if (ishdf5) {
484 #if defined(PETSC_HAVE_HDF5)
485     ierr = VecLoad_Plex_HDF5_Internal(v, viewer);CHKERRQ(ierr);
486 #else
487     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
488 #endif
489   } else {
490     ierr = VecLoad_Default(v, viewer);CHKERRQ(ierr);
491   }
492   PetscFunctionReturn(0);
493 }
494 
495 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
496 {
497   DM                dm;
498   PetscViewerFormat format;
499   PetscBool         ishdf5;
500   PetscErrorCode    ierr;
501 
502   PetscFunctionBegin;
503   ierr = VecGetDM(originalv, &dm);CHKERRQ(ierr);
504   if (!dm) SETERRQ(PetscObjectComm((PetscObject) originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
505   ierr = PetscViewerGetFormat(viewer, &format);CHKERRQ(ierr);
506   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5);CHKERRQ(ierr);
507   if (format == PETSC_VIEWER_NATIVE) {
508     if (dm->useNatural) {
509       if (dm->sfNatural) {
510         if (ishdf5) {
511 #if defined(PETSC_HAVE_HDF5)
512           Vec         v;
513           const char *vecname;
514 
515           ierr = DMGetGlobalVector(dm, &v);CHKERRQ(ierr);
516           ierr = PetscObjectGetName((PetscObject) originalv, &vecname);CHKERRQ(ierr);
517           ierr = PetscObjectSetName((PetscObject) v, vecname);CHKERRQ(ierr);
518           ierr = VecLoad_Plex_HDF5_Native_Internal(v, viewer);CHKERRQ(ierr);
519           ierr = DMPlexNaturalToGlobalBegin(dm, v, originalv);CHKERRQ(ierr);
520           ierr = DMPlexNaturalToGlobalEnd(dm, v, originalv);CHKERRQ(ierr);
521           ierr = DMRestoreGlobalVector(dm, &v);CHKERRQ(ierr);
522 #else
523           SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
524 #endif
525         } else SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
526       }
527     } else {
528       ierr = VecLoad_Default(originalv, viewer);CHKERRQ(ierr);
529     }
530   }
531   PetscFunctionReturn(0);
532 }
533 
534 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
535 {
536   PetscSection       coordSection;
537   Vec                coordinates;
538   DMLabel            depthLabel, celltypeLabel;
539   const char        *name[4];
540   const PetscScalar *a;
541   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
542   PetscErrorCode     ierr;
543 
544   PetscFunctionBegin;
545   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
546   ierr = DMGetCoordinatesLocal(dm, &coordinates);CHKERRQ(ierr);
547   ierr = DMGetCoordinateSection(dm, &coordSection);CHKERRQ(ierr);
548   ierr = DMPlexGetDepthLabel(dm, &depthLabel);CHKERRQ(ierr);
549   ierr = DMPlexGetCellTypeLabel(dm, &celltypeLabel);CHKERRQ(ierr);
550   ierr = DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd);CHKERRQ(ierr);
551   ierr = PetscSectionGetChart(coordSection, &pStart, &pEnd);CHKERRQ(ierr);
552   ierr = VecGetArrayRead(coordinates, &a);CHKERRQ(ierr);
553   name[0]     = "vertex";
554   name[1]     = "edge";
555   name[dim-1] = "face";
556   name[dim]   = "cell";
557   for (c = cStart; c < cEnd; ++c) {
558     PetscInt *closure = NULL;
559     PetscInt  closureSize, cl, ct;
560 
561     ierr = DMLabelGetValue(celltypeLabel, c, &ct);CHKERRQ(ierr);
562     ierr = PetscViewerASCIIPrintf(viewer, "Geometry for cell %D polytope type %s:\n", c, DMPolytopeTypes[ct]);CHKERRQ(ierr);
563     ierr = DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure);CHKERRQ(ierr);
564     ierr = PetscViewerASCIIPushTab(viewer);CHKERRQ(ierr);
565     for (cl = 0; cl < closureSize*2; cl += 2) {
566       PetscInt point = closure[cl], depth, dof, off, d, p;
567 
568       if ((point < pStart) || (point >= pEnd)) continue;
569       ierr = PetscSectionGetDof(coordSection, point, &dof);CHKERRQ(ierr);
570       if (!dof) continue;
571       ierr = DMLabelGetValue(depthLabel, point, &depth);CHKERRQ(ierr);
572       ierr = PetscSectionGetOffset(coordSection, point, &off);CHKERRQ(ierr);
573       ierr = PetscViewerASCIIPrintf(viewer, "%s %D coords:", name[depth], point);CHKERRQ(ierr);
574       for (p = 0; p < dof/dim; ++p) {
575         ierr = PetscViewerASCIIPrintf(viewer, " (");CHKERRQ(ierr);
576         for (d = 0; d < dim; ++d) {
577           if (d > 0) {ierr = PetscViewerASCIIPrintf(viewer, ", ");CHKERRQ(ierr);}
578           ierr = PetscViewerASCIIPrintf(viewer, "%g", (double) PetscRealPart(a[off+p*dim+d]));CHKERRQ(ierr);
579         }
580         ierr = PetscViewerASCIIPrintf(viewer, ")");CHKERRQ(ierr);
581       }
582       ierr = PetscViewerASCIIPrintf(viewer, "\n");CHKERRQ(ierr);
583     }
584     ierr = DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure);CHKERRQ(ierr);
585     ierr = PetscViewerASCIIPopTab(viewer);CHKERRQ(ierr);
586   }
587   ierr = VecRestoreArrayRead(coordinates, &a);CHKERRQ(ierr);
588   PetscFunctionReturn(0);
589 }
590 
591 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
592 {
593   DM_Plex          *mesh = (DM_Plex*) dm->data;
594   DM                cdm;
595   DMLabel           markers, celltypes;
596   PetscSection      coordSection;
597   Vec               coordinates;
598   PetscViewerFormat format;
599   PetscErrorCode    ierr;
600 
601   PetscFunctionBegin;
602   ierr = DMGetCoordinateDM(dm, &cdm);CHKERRQ(ierr);
603   ierr = DMGetLocalSection(cdm, &coordSection);CHKERRQ(ierr);
604   ierr = DMGetCoordinatesLocal(dm, &coordinates);CHKERRQ(ierr);
605   ierr = PetscViewerGetFormat(viewer, &format);CHKERRQ(ierr);
606   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
607     const char *name;
608     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
609     PetscInt    pStart, pEnd, p;
610     PetscMPIInt rank, size;
611 
612     ierr = MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank);CHKERRQ(ierr);
613     ierr = MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size);CHKERRQ(ierr);
614     ierr = PetscObjectGetName((PetscObject) dm, &name);CHKERRQ(ierr);
615     ierr = DMPlexGetChart(dm, &pStart, &pEnd);CHKERRQ(ierr);
616     ierr = DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize);CHKERRQ(ierr);
617     ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
618     ierr = DMPlexGetVTKCellHeight(dm, &cellHeight);CHKERRQ(ierr);
619     if (name) {ierr = PetscViewerASCIIPrintf(viewer, "%s in %D dimension%s:\n", name, dim, dim == 1 ? "" : "s");CHKERRQ(ierr);}
620     else      {ierr = PetscViewerASCIIPrintf(viewer, "Mesh in %D dimension%s:\n", dim, dim == 1 ? "" : "s");CHKERRQ(ierr);}
621     if (cellHeight) {ierr = PetscViewerASCIIPrintf(viewer, "  Cells are at height %D\n", cellHeight);CHKERRQ(ierr);}
622     ierr = PetscViewerASCIIPrintf(viewer, "Supports:\n", name);CHKERRQ(ierr);
623     ierr = PetscViewerASCIIPushSynchronized(viewer);CHKERRQ(ierr);
624     ierr = PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %D\n", rank, maxSupportSize);CHKERRQ(ierr);
625     for (p = pStart; p < pEnd; ++p) {
626       PetscInt dof, off, s;
627 
628       ierr = PetscSectionGetDof(mesh->supportSection, p, &dof);CHKERRQ(ierr);
629       ierr = PetscSectionGetOffset(mesh->supportSection, p, &off);CHKERRQ(ierr);
630       for (s = off; s < off+dof; ++s) {
631         ierr = PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %D ----> %D\n", rank, p, mesh->supports[s]);CHKERRQ(ierr);
632       }
633     }
634     ierr = PetscViewerFlush(viewer);CHKERRQ(ierr);
635     ierr = PetscViewerASCIIPrintf(viewer, "Cones:\n", name);CHKERRQ(ierr);
636     ierr = PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %D\n", rank, maxConeSize);CHKERRQ(ierr);
637     for (p = pStart; p < pEnd; ++p) {
638       PetscInt dof, off, c;
639 
640       ierr = PetscSectionGetDof(mesh->coneSection, p, &dof);CHKERRQ(ierr);
641       ierr = PetscSectionGetOffset(mesh->coneSection, p, &off);CHKERRQ(ierr);
642       for (c = off; c < off+dof; ++c) {
643         ierr = PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %D <---- %D (%D)\n", rank, p, mesh->cones[c], mesh->coneOrientations[c]);CHKERRQ(ierr);
644       }
645     }
646     ierr = PetscViewerFlush(viewer);CHKERRQ(ierr);
647     ierr = PetscViewerASCIIPopSynchronized(viewer);CHKERRQ(ierr);
648     if (coordSection && coordinates) {
649       ierr = PetscSectionVecView(coordSection, coordinates, viewer);CHKERRQ(ierr);
650     }
651     ierr = DMGetLabel(dm, "marker", &markers);CHKERRQ(ierr);
652     if (markers) {ierr = DMLabelView(markers,viewer);CHKERRQ(ierr);}
653     ierr = DMPlexGetCellTypeLabel(dm, &celltypes);CHKERRQ(ierr);
654     if (celltypes) {ierr = DMLabelView(celltypes, viewer);CHKERRQ(ierr);}
655     if (size > 1) {
656       PetscSF sf;
657 
658       ierr = DMGetPointSF(dm, &sf);CHKERRQ(ierr);
659       ierr = PetscSFView(sf, viewer);CHKERRQ(ierr);
660     }
661     ierr = PetscViewerFlush(viewer);CHKERRQ(ierr);
662   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
663     const char  *name, *color;
664     const char  *defcolors[3]  = {"gray", "orange", "green"};
665     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
666     char         lname[PETSC_MAX_PATH_LEN];
667     PetscReal    scale         = 2.0;
668     PetscReal    tikzscale     = 1.0;
669     PetscBool    useNumbers    = PETSC_TRUE, useLabels, useColors;
670     double       tcoords[3];
671     PetscScalar *coords;
672     PetscInt     numLabels, l, numColors, numLColors, dim, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p;
673     PetscMPIInt  rank, size;
674     char         **names, **colors, **lcolors;
675     PetscBool    plotEdges, flg, lflg;
676     PetscBT      wp = NULL;
677     PetscInt     pEnd, pStart;
678 
679     ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
680     ierr = DMPlexGetDepth(dm, &depth);CHKERRQ(ierr);
681     ierr = DMGetNumLabels(dm, &numLabels);CHKERRQ(ierr);
682     numLabels  = PetscMax(numLabels, 10);
683     numColors  = 10;
684     numLColors = 10;
685     ierr = PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors);CHKERRQ(ierr);
686     ierr = PetscOptionsGetReal(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_scale", &scale, NULL);CHKERRQ(ierr);
687     ierr = PetscOptionsGetReal(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL);CHKERRQ(ierr);
688     ierr = PetscOptionsGetBool(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL);CHKERRQ(ierr);
689     ierr = PetscOptionsGetStringArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels);CHKERRQ(ierr);
690     if (!useLabels) numLabels = 0;
691     ierr = PetscOptionsGetStringArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors);CHKERRQ(ierr);
692     if (!useColors) {
693       numColors = 3;
694       for (c = 0; c < numColors; ++c) {ierr = PetscStrallocpy(defcolors[c], &colors[c]);CHKERRQ(ierr);}
695     }
696     ierr = PetscOptionsGetStringArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors);CHKERRQ(ierr);
697     if (!useColors) {
698       numLColors = 4;
699       for (c = 0; c < numLColors; ++c) {ierr = PetscStrallocpy(deflcolors[c], &lcolors[c]);CHKERRQ(ierr);}
700     }
701     ierr = PetscOptionsGetString(((PetscObject) viewer)->options, ((PetscObject) viewer)->prefix, "-dm_plex_view_label_filter", lname, PETSC_MAX_PATH_LEN, &lflg);CHKERRQ(ierr);
702     plotEdges = (PetscBool)(depth > 1 && useNumbers && dim < 3);
703     ierr = PetscOptionsGetBool(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg);CHKERRQ(ierr);
704     if (flg && plotEdges && depth < dim) SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Mesh must be interpolated");
705     if (depth < dim) plotEdges = PETSC_FALSE;
706 
707     /* filter points with labelvalue != labeldefaultvalue */
708     ierr = DMPlexGetChart(dm, &pStart, &pEnd);CHKERRQ(ierr);
709     if (lflg) {
710       DMLabel lbl;
711 
712       ierr = DMGetLabel(dm, lname, &lbl);CHKERRQ(ierr);
713       if (lbl) {
714         PetscInt val, defval;
715 
716         ierr = DMLabelGetDefaultValue(lbl, &defval);CHKERRQ(ierr);
717         ierr = PetscBTCreate(pEnd-pStart, &wp);CHKERRQ(ierr);
718         for (c = pStart;  c < pEnd; c++) {
719           PetscInt *closure = NULL;
720           PetscInt  closureSize;
721 
722           ierr = DMLabelGetValue(lbl, c, &val);CHKERRQ(ierr);
723           if (val == defval) continue;
724 
725           ierr = DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure);CHKERRQ(ierr);
726           for (p = 0; p < closureSize*2; p += 2) {
727             ierr = PetscBTSet(wp, closure[p] - pStart);CHKERRQ(ierr);
728           }
729           ierr = DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure);CHKERRQ(ierr);
730         }
731       }
732     }
733 
734     ierr = MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank);CHKERRQ(ierr);
735     ierr = MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size);CHKERRQ(ierr);
736     ierr = PetscObjectGetName((PetscObject) dm, &name);CHKERRQ(ierr);
737     ierr = PetscViewerASCIIPrintf(viewer, "\
738 \\documentclass[tikz]{standalone}\n\n\
739 \\usepackage{pgflibraryshapes}\n\
740 \\usetikzlibrary{backgrounds}\n\
741 \\usetikzlibrary{arrows}\n\
742 \\begin{document}\n");CHKERRQ(ierr);
743     if (size > 1) {
744       ierr = PetscViewerASCIIPrintf(viewer, "%s for process ", name);CHKERRQ(ierr);
745       for (p = 0; p < size; ++p) {
746         if (p > 0 && p == size-1) {
747           ierr = PetscViewerASCIIPrintf(viewer, ", and ", colors[p%numColors], p);CHKERRQ(ierr);
748         } else if (p > 0) {
749           ierr = PetscViewerASCIIPrintf(viewer, ", ", colors[p%numColors], p);CHKERRQ(ierr);
750         }
751         ierr = PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%D}", colors[p%numColors], p);CHKERRQ(ierr);
752       }
753       ierr = PetscViewerASCIIPrintf(viewer, ".\n\n\n");CHKERRQ(ierr);
754     }
755     ierr = PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double) tikzscale);CHKERRQ(ierr);
756 
757     /* Plot vertices */
758     ierr = DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd);CHKERRQ(ierr);
759     ierr = VecGetArray(coordinates, &coords);CHKERRQ(ierr);
760     ierr = PetscViewerASCIIPushSynchronized(viewer);CHKERRQ(ierr);
761     for (v = vStart; v < vEnd; ++v) {
762       PetscInt  off, dof, d;
763       PetscBool isLabeled = PETSC_FALSE;
764 
765       if (wp && !PetscBTLookup(wp,v - pStart)) continue;
766       ierr = PetscSectionGetDof(coordSection, v, &dof);CHKERRQ(ierr);
767       ierr = PetscSectionGetOffset(coordSection, v, &off);CHKERRQ(ierr);
768       ierr = PetscViewerASCIISynchronizedPrintf(viewer, "\\path (");CHKERRQ(ierr);
769       if (PetscUnlikely(dof > 3)) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_PLIB,"coordSection vertex %D has dof %D > 3",v,dof);
770       for (d = 0; d < dof; ++d) {
771         tcoords[d] = (double) (scale*PetscRealPart(coords[off+d]));
772         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
773       }
774       /* Rotate coordinates since PGF makes z point out of the page instead of up */
775       if (dim == 3) {PetscReal tmp = tcoords[1]; tcoords[1] = tcoords[2]; tcoords[2] = -tmp;}
776       for (d = 0; d < dof; ++d) {
777         if (d > 0) {ierr = PetscViewerASCIISynchronizedPrintf(viewer, ",");CHKERRQ(ierr);}
778         ierr = PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double) tcoords[d]);CHKERRQ(ierr);
779       }
780       color = colors[rank%numColors];
781       for (l = 0; l < numLabels; ++l) {
782         PetscInt val;
783         ierr = DMGetLabelValue(dm, names[l], v, &val);CHKERRQ(ierr);
784         if (val >= 0) {color = lcolors[l%numLColors]; isLabeled = PETSC_TRUE; break;}
785       }
786       if (useNumbers) {
787         ierr = PetscViewerASCIISynchronizedPrintf(viewer, ") node(%D_%d) [draw,shape=circle,color=%s] {%D};\n", v, rank, color, v);CHKERRQ(ierr);
788       } else {
789         ierr = PetscViewerASCIISynchronizedPrintf(viewer, ") node(%D_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color);CHKERRQ(ierr);
790       }
791     }
792     ierr = VecRestoreArray(coordinates, &coords);CHKERRQ(ierr);
793     ierr = PetscViewerFlush(viewer);CHKERRQ(ierr);
794     /* Plot cells */
795     ierr = DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd);CHKERRQ(ierr);
796     ierr = DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd);CHKERRQ(ierr);
797     if (dim == 3 || !useNumbers) {
798       for (e = eStart; e < eEnd; ++e) {
799         const PetscInt *cone;
800 
801         if (wp && !PetscBTLookup(wp,e - pStart)) continue;
802         color = colors[rank%numColors];
803         for (l = 0; l < numLabels; ++l) {
804           PetscInt val;
805           ierr = DMGetLabelValue(dm, names[l], e, &val);CHKERRQ(ierr);
806           if (val >= 0) {color = lcolors[l%numLColors]; break;}
807         }
808         ierr = DMPlexGetCone(dm, e, &cone);CHKERRQ(ierr);
809         ierr = PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%D_%d) -- (%D_%d);\n", color, cone[0], rank, cone[1], rank);CHKERRQ(ierr);
810       }
811     } else {
812       for (c = cStart; c < cEnd; ++c) {
813         PetscInt *closure = NULL;
814         PetscInt  closureSize, firstPoint = -1;
815 
816         if (wp && !PetscBTLookup(wp,c - pStart)) continue;
817         ierr = DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure);CHKERRQ(ierr);
818         ierr = PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank%numColors]);CHKERRQ(ierr);
819         for (p = 0; p < closureSize*2; p += 2) {
820           const PetscInt point = closure[p];
821 
822           if ((point < vStart) || (point >= vEnd)) continue;
823           if (firstPoint >= 0) {ierr = PetscViewerASCIISynchronizedPrintf(viewer, " -- ");CHKERRQ(ierr);}
824           ierr = PetscViewerASCIISynchronizedPrintf(viewer, "(%D_%d)", point, rank);CHKERRQ(ierr);
825           if (firstPoint < 0) firstPoint = point;
826         }
827         /* Why doesn't this work? ierr = PetscViewerASCIISynchronizedPrintf(viewer, " -- cycle;\n");CHKERRQ(ierr); */
828         ierr = PetscViewerASCIISynchronizedPrintf(viewer, " -- (%D_%d);\n", firstPoint, rank);CHKERRQ(ierr);
829         ierr = DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure);CHKERRQ(ierr);
830       }
831     }
832     ierr = VecGetArray(coordinates, &coords);CHKERRQ(ierr);
833     for (c = cStart; c < cEnd; ++c) {
834       double    ccoords[3] = {0.0, 0.0, 0.0};
835       PetscBool isLabeled  = PETSC_FALSE;
836       PetscInt *closure    = NULL;
837       PetscInt  closureSize, dof, d, n = 0;
838 
839       if (wp && !PetscBTLookup(wp,c - pStart)) continue;
840       ierr = DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure);CHKERRQ(ierr);
841       ierr = PetscViewerASCIISynchronizedPrintf(viewer, "\\path (");CHKERRQ(ierr);
842       for (p = 0; p < closureSize*2; p += 2) {
843         const PetscInt point = closure[p];
844         PetscInt       off;
845 
846         if ((point < vStart) || (point >= vEnd)) continue;
847         ierr = PetscSectionGetDof(coordSection, point, &dof);CHKERRQ(ierr);
848         ierr = PetscSectionGetOffset(coordSection, point, &off);CHKERRQ(ierr);
849         for (d = 0; d < dof; ++d) {
850           tcoords[d] = (double) (scale*PetscRealPart(coords[off+d]));
851           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
852         }
853         /* Rotate coordinates since PGF makes z point out of the page instead of up */
854         if (dof == 3) {PetscReal tmp = tcoords[1]; tcoords[1] = tcoords[2]; tcoords[2] = -tmp;}
855         for (d = 0; d < dof; ++d) {ccoords[d] += tcoords[d];}
856         ++n;
857       }
858       for (d = 0; d < dof; ++d) {ccoords[d] /= n;}
859       ierr = DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure);CHKERRQ(ierr);
860       for (d = 0; d < dof; ++d) {
861         if (d > 0) {ierr = PetscViewerASCIISynchronizedPrintf(viewer, ",");CHKERRQ(ierr);}
862         ierr = PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double) ccoords[d]);CHKERRQ(ierr);
863       }
864       color = colors[rank%numColors];
865       for (l = 0; l < numLabels; ++l) {
866         PetscInt val;
867         ierr = DMGetLabelValue(dm, names[l], c, &val);CHKERRQ(ierr);
868         if (val >= 0) {color = lcolors[l%numLColors]; isLabeled = PETSC_TRUE; break;}
869       }
870       if (useNumbers) {
871         ierr = PetscViewerASCIISynchronizedPrintf(viewer, ") node(%D_%d) [draw,shape=circle,color=%s] {%D};\n", c, rank, color, c);CHKERRQ(ierr);
872       } else {
873         ierr = PetscViewerASCIISynchronizedPrintf(viewer, ") node(%D_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color);CHKERRQ(ierr);
874       }
875     }
876     ierr = VecRestoreArray(coordinates, &coords);CHKERRQ(ierr);
877     /* Plot edges */
878     if (plotEdges) {
879       ierr = VecGetArray(coordinates, &coords);CHKERRQ(ierr);
880       ierr = PetscViewerASCIIPrintf(viewer, "\\path\n");CHKERRQ(ierr);
881       for (e = eStart; e < eEnd; ++e) {
882         const PetscInt *cone;
883         PetscInt        coneSize, offA, offB, dof, d;
884 
885         if (wp && !PetscBTLookup(wp,e - pStart)) continue;
886         ierr = DMPlexGetConeSize(dm, e, &coneSize);CHKERRQ(ierr);
887         if (coneSize != 2) SETERRQ2(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %D cone should have two vertices, not %D", e, coneSize);
888         ierr = DMPlexGetCone(dm, e, &cone);CHKERRQ(ierr);
889         ierr = PetscSectionGetDof(coordSection, cone[0], &dof);CHKERRQ(ierr);
890         ierr = PetscSectionGetOffset(coordSection, cone[0], &offA);CHKERRQ(ierr);
891         ierr = PetscSectionGetOffset(coordSection, cone[1], &offB);CHKERRQ(ierr);
892         ierr = PetscViewerASCIISynchronizedPrintf(viewer, "(");CHKERRQ(ierr);
893         for (d = 0; d < dof; ++d) {
894           tcoords[d] = (double) (0.5*scale*PetscRealPart(coords[offA+d]+coords[offB+d]));
895           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
896         }
897         /* Rotate coordinates since PGF makes z point out of the page instead of up */
898         if (dim == 3) {PetscReal tmp = tcoords[1]; tcoords[1] = tcoords[2]; tcoords[2] = -tmp;}
899         for (d = 0; d < dof; ++d) {
900           if (d > 0) {ierr = PetscViewerASCIISynchronizedPrintf(viewer, ",");CHKERRQ(ierr);}
901           ierr = PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]);CHKERRQ(ierr);
902         }
903         color = colors[rank%numColors];
904         for (l = 0; l < numLabels; ++l) {
905           PetscInt val;
906           ierr = DMGetLabelValue(dm, names[l], v, &val);CHKERRQ(ierr);
907           if (val >= 0) {color = lcolors[l%numLColors]; break;}
908         }
909         ierr = PetscViewerASCIISynchronizedPrintf(viewer, ") node(%D_%d) [draw,shape=circle,color=%s] {%D} --\n", e, rank, color, e);CHKERRQ(ierr);
910       }
911       ierr = VecRestoreArray(coordinates, &coords);CHKERRQ(ierr);
912       ierr = PetscViewerFlush(viewer);CHKERRQ(ierr);
913       ierr = PetscViewerASCIIPrintf(viewer, "(0,0);\n");CHKERRQ(ierr);
914     }
915     ierr = PetscViewerFlush(viewer);CHKERRQ(ierr);
916     ierr = PetscViewerASCIIPopSynchronized(viewer);CHKERRQ(ierr);
917     ierr = PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n");CHKERRQ(ierr);
918     ierr = PetscViewerASCIIPrintf(viewer, "\\end{document}\n", name);CHKERRQ(ierr);
919     for (l = 0; l < numLabels;  ++l) {ierr = PetscFree(names[l]);CHKERRQ(ierr);}
920     for (c = 0; c < numColors;  ++c) {ierr = PetscFree(colors[c]);CHKERRQ(ierr);}
921     for (c = 0; c < numLColors; ++c) {ierr = PetscFree(lcolors[c]);CHKERRQ(ierr);}
922     ierr = PetscFree3(names, colors, lcolors);CHKERRQ(ierr);
923     ierr = PetscBTDestroy(&wp);CHKERRQ(ierr);
924   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
925     Vec                    cown,acown;
926     VecScatter             sct;
927     ISLocalToGlobalMapping g2l;
928     IS                     gid,acis;
929     MPI_Comm               comm,ncomm = MPI_COMM_NULL;
930     MPI_Group              ggroup,ngroup;
931     PetscScalar            *array,nid;
932     const PetscInt         *idxs;
933     PetscInt               *idxs2,*start,*adjacency,*work;
934     PetscInt64             lm[3],gm[3];
935     PetscInt               i,c,cStart,cEnd,cum,numVertices,ect,ectn,cellHeight;
936     PetscMPIInt            d1,d2,rank;
937 
938     ierr = PetscObjectGetComm((PetscObject)dm,&comm);CHKERRQ(ierr);
939     ierr = MPI_Comm_rank(comm,&rank);CHKERRQ(ierr);
940 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
941     ierr = MPI_Comm_split_type(comm,MPI_COMM_TYPE_SHARED,rank,MPI_INFO_NULL,&ncomm);CHKERRQ(ierr);
942 #endif
943     if (ncomm != MPI_COMM_NULL) {
944       ierr = MPI_Comm_group(comm,&ggroup);CHKERRQ(ierr);
945       ierr = MPI_Comm_group(ncomm,&ngroup);CHKERRQ(ierr);
946       d1   = 0;
947       ierr = MPI_Group_translate_ranks(ngroup,1,&d1,ggroup,&d2);CHKERRQ(ierr);
948       nid  = d2;
949       ierr = MPI_Group_free(&ggroup);CHKERRQ(ierr);
950       ierr = MPI_Group_free(&ngroup);CHKERRQ(ierr);
951       ierr = MPI_Comm_free(&ncomm);CHKERRQ(ierr);
952     } else nid = 0.0;
953 
954     /* Get connectivity */
955     ierr = DMPlexGetVTKCellHeight(dm,&cellHeight);CHKERRQ(ierr);
956     ierr = DMPlexCreatePartitionerGraph(dm,cellHeight,&numVertices,&start,&adjacency,&gid);CHKERRQ(ierr);
957 
958     /* filter overlapped local cells */
959     ierr = DMPlexGetHeightStratum(dm,cellHeight,&cStart,&cEnd);CHKERRQ(ierr);
960     ierr = ISGetIndices(gid,&idxs);CHKERRQ(ierr);
961     ierr = ISGetLocalSize(gid,&cum);CHKERRQ(ierr);
962     ierr = PetscMalloc1(cum,&idxs2);CHKERRQ(ierr);
963     for (c = cStart, cum = 0; c < cEnd; c++) {
964       if (idxs[c-cStart] < 0) continue;
965       idxs2[cum++] = idxs[c-cStart];
966     }
967     ierr = ISRestoreIndices(gid,&idxs);CHKERRQ(ierr);
968     if (numVertices != cum) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_PLIB,"Unexpected %D != %D",numVertices,cum);
969     ierr = ISDestroy(&gid);CHKERRQ(ierr);
970     ierr = ISCreateGeneral(comm,numVertices,idxs2,PETSC_OWN_POINTER,&gid);CHKERRQ(ierr);
971 
972     /* support for node-aware cell locality */
973     ierr = ISCreateGeneral(comm,start[numVertices],adjacency,PETSC_USE_POINTER,&acis);CHKERRQ(ierr);
974     ierr = VecCreateSeq(PETSC_COMM_SELF,start[numVertices],&acown);CHKERRQ(ierr);
975     ierr = VecCreateMPI(comm,numVertices,PETSC_DECIDE,&cown);CHKERRQ(ierr);
976     ierr = VecGetArray(cown,&array);CHKERRQ(ierr);
977     for (c = 0; c < numVertices; c++) array[c] = nid;
978     ierr = VecRestoreArray(cown,&array);CHKERRQ(ierr);
979     ierr = VecScatterCreate(cown,acis,acown,NULL,&sct);CHKERRQ(ierr);
980     ierr = VecScatterBegin(sct,cown,acown,INSERT_VALUES,SCATTER_FORWARD);CHKERRQ(ierr);
981     ierr = VecScatterEnd(sct,cown,acown,INSERT_VALUES,SCATTER_FORWARD);CHKERRQ(ierr);
982     ierr = ISDestroy(&acis);CHKERRQ(ierr);
983     ierr = VecScatterDestroy(&sct);CHKERRQ(ierr);
984     ierr = VecDestroy(&cown);CHKERRQ(ierr);
985 
986     /* compute edgeCut */
987     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum,start[c+1]-start[c]);
988     ierr = PetscMalloc1(cum,&work);CHKERRQ(ierr);
989     ierr = ISLocalToGlobalMappingCreateIS(gid,&g2l);CHKERRQ(ierr);
990     ierr = ISLocalToGlobalMappingSetType(g2l,ISLOCALTOGLOBALMAPPINGHASH);CHKERRQ(ierr);
991     ierr = ISDestroy(&gid);CHKERRQ(ierr);
992     ierr = VecGetArray(acown,&array);CHKERRQ(ierr);
993     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
994       PetscInt totl;
995 
996       totl = start[c+1]-start[c];
997       ierr = ISGlobalToLocalMappingApply(g2l,IS_GTOLM_MASK,totl,adjacency+start[c],NULL,work);CHKERRQ(ierr);
998       for (i = 0; i < totl; i++) {
999         if (work[i] < 0) {
1000           ect  += 1;
1001           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1002         }
1003       }
1004     }
1005     ierr  = PetscFree(work);CHKERRQ(ierr);
1006     ierr  = VecRestoreArray(acown,&array);CHKERRQ(ierr);
1007     lm[0] = numVertices > 0 ?  numVertices : PETSC_MAX_INT;
1008     lm[1] = -numVertices;
1009     ierr  = MPIU_Allreduce(lm,gm,2,MPIU_INT64,MPI_MIN,comm);CHKERRQ(ierr);
1010     ierr  = PetscViewerASCIIPrintf(viewer,"  Cell balance: %.2f (max %D, min %D",-((double)gm[1])/((double)gm[0]),-(PetscInt)gm[1],(PetscInt)gm[0]);CHKERRQ(ierr);
1011     lm[0] = ect; /* edgeCut */
1012     lm[1] = ectn; /* node-aware edgeCut */
1013     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1014     ierr  = MPIU_Allreduce(lm,gm,3,MPIU_INT64,MPI_SUM,comm);CHKERRQ(ierr);
1015     ierr  = PetscViewerASCIIPrintf(viewer,", empty %D)\n",(PetscInt)gm[2]);CHKERRQ(ierr);
1016 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1017     ierr  = PetscViewerASCIIPrintf(viewer,"  Edge Cut: %D (on node %.3f)\n",(PetscInt)(gm[0]/2),gm[0] ? ((double)(gm[1]))/((double)gm[0]) : 1.);CHKERRQ(ierr);
1018 #else
1019     ierr  = PetscViewerASCIIPrintf(viewer,"  Edge Cut: %D (on node %.3f)\n",(PetscInt)(gm[0]/2),0.0);CHKERRQ(ierr);
1020 #endif
1021     ierr  = ISLocalToGlobalMappingDestroy(&g2l);CHKERRQ(ierr);
1022     ierr  = PetscFree(start);CHKERRQ(ierr);
1023     ierr  = PetscFree(adjacency);CHKERRQ(ierr);
1024     ierr  = VecDestroy(&acown);CHKERRQ(ierr);
1025   } else {
1026     const char    *name;
1027     PetscInt      *sizes, *hybsizes, *ghostsizes;
1028     PetscInt       locDepth, depth, cellHeight, dim, d;
1029     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1030     PetscInt       numLabels, l;
1031     DMPolytopeType ct0;
1032     MPI_Comm       comm;
1033     PetscMPIInt    size, rank;
1034 
1035     ierr = PetscObjectGetComm((PetscObject) dm, &comm);CHKERRQ(ierr);
1036     ierr = MPI_Comm_size(comm, &size);CHKERRQ(ierr);
1037     ierr = MPI_Comm_rank(comm, &rank);CHKERRQ(ierr);
1038     ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
1039     ierr = DMPlexGetVTKCellHeight(dm, &cellHeight);CHKERRQ(ierr);
1040     ierr = PetscObjectGetName((PetscObject) dm, &name);CHKERRQ(ierr);
1041     if (name) {ierr = PetscViewerASCIIPrintf(viewer, "%s in %D dimension%s:\n", name, dim, dim == 1 ? "" : "s");CHKERRQ(ierr);}
1042     else      {ierr = PetscViewerASCIIPrintf(viewer, "Mesh in %D dimension%s:\n", dim, dim == 1 ? "" : "s");CHKERRQ(ierr);}
1043     if (cellHeight) {ierr = PetscViewerASCIIPrintf(viewer, "  Cells are at height %D\n", cellHeight);CHKERRQ(ierr);}
1044     ierr = DMPlexGetDepth(dm, &locDepth);CHKERRQ(ierr);
1045     ierr = MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm);CHKERRQ(ierr);
1046     ierr = DMPlexGetGhostCellStratum(dm, &gcStart, &gcEnd);CHKERRQ(ierr);
1047     gcNum = gcEnd - gcStart;
1048     ierr = PetscCalloc3(size,&sizes,size,&hybsizes,size,&ghostsizes);CHKERRQ(ierr);
1049     for (d = 0; d <= depth; d++) {
1050       PetscInt Nc[2] = {0, 0}, ict;
1051 
1052       ierr = DMPlexGetDepthStratum(dm, d, &pStart, &pEnd);CHKERRQ(ierr);
1053       ierr = DMPlexGetCellType(dm, pStart, &ct0);CHKERRQ(ierr);
1054       ict  = ct0;
1055       ierr = MPI_Bcast(&ict, 1, MPIU_INT, 0, comm);CHKERRQ(ierr);
1056       ct0  = (DMPolytopeType) ict;
1057       for (p = pStart; p < pEnd; ++p) {
1058         DMPolytopeType ct;
1059 
1060         ierr = DMPlexGetCellType(dm, p, &ct);CHKERRQ(ierr);
1061         if (ct == ct0) ++Nc[0];
1062         else           ++Nc[1];
1063       }
1064       ierr = MPI_Gather(&Nc[0], 1, MPIU_INT, sizes,    1, MPIU_INT, 0, comm);CHKERRQ(ierr);
1065       ierr = MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm);CHKERRQ(ierr);
1066       if (d == depth) {ierr = MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm);CHKERRQ(ierr);}
1067       ierr = PetscViewerASCIIPrintf(viewer, "  %D-cells:", (depth == 1) && d ? dim : d);CHKERRQ(ierr);
1068       for (p = 0; p < size; ++p) {
1069         if (!rank) {
1070           ierr = PetscViewerASCIIPrintf(viewer, " %D", sizes[p]+hybsizes[p]);CHKERRQ(ierr);
1071           if (hybsizes[p]   > 0) {ierr = PetscViewerASCIIPrintf(viewer, " (%D)", hybsizes[p]);CHKERRQ(ierr);}
1072           if (ghostsizes[p] > 0) {ierr = PetscViewerASCIIPrintf(viewer, " [%D]", ghostsizes[p]);CHKERRQ(ierr);}
1073         }
1074       }
1075       ierr = PetscViewerASCIIPrintf(viewer, "\n");CHKERRQ(ierr);
1076     }
1077     ierr = PetscFree3(sizes,hybsizes,ghostsizes);CHKERRQ(ierr);
1078     ierr = DMGetNumLabels(dm, &numLabels);CHKERRQ(ierr);
1079     if (numLabels) {ierr = PetscViewerASCIIPrintf(viewer, "Labels:\n");CHKERRQ(ierr);}
1080     for (l = 0; l < numLabels; ++l) {
1081       DMLabel         label;
1082       const char     *name;
1083       IS              valueIS;
1084       const PetscInt *values;
1085       PetscInt        numValues, v;
1086 
1087       ierr = DMGetLabelName(dm, l, &name);CHKERRQ(ierr);
1088       ierr = DMGetLabel(dm, name, &label);CHKERRQ(ierr);
1089       ierr = DMLabelGetNumValues(label, &numValues);CHKERRQ(ierr);
1090       ierr = PetscViewerASCIIPrintf(viewer, "  %s: %D strata with value/size (", name, numValues);CHKERRQ(ierr);
1091       ierr = DMLabelGetValueIS(label, &valueIS);CHKERRQ(ierr);
1092       ierr = ISGetIndices(valueIS, &values);CHKERRQ(ierr);
1093       ierr = PetscViewerASCIIUseTabs(viewer, PETSC_FALSE);CHKERRQ(ierr);
1094       for (v = 0; v < numValues; ++v) {
1095         PetscInt size;
1096 
1097         ierr = DMLabelGetStratumSize(label, values[v], &size);CHKERRQ(ierr);
1098         if (v > 0) {ierr = PetscViewerASCIIPrintf(viewer, ", ");CHKERRQ(ierr);}
1099         ierr = PetscViewerASCIIPrintf(viewer, "%D (%D)", values[v], size);CHKERRQ(ierr);
1100       }
1101       ierr = PetscViewerASCIIPrintf(viewer, ")\n");CHKERRQ(ierr);
1102       ierr = PetscViewerASCIIUseTabs(viewer, PETSC_TRUE);CHKERRQ(ierr);
1103       ierr = ISRestoreIndices(valueIS, &values);CHKERRQ(ierr);
1104       ierr = ISDestroy(&valueIS);CHKERRQ(ierr);
1105     }
1106     /* If no fields are specified, people do not want to see adjacency */
1107     if (dm->Nf) {
1108       PetscInt f;
1109 
1110       for (f = 0; f < dm->Nf; ++f) {
1111         const char *name;
1112 
1113         ierr = PetscObjectGetName(dm->fields[f].disc, &name);CHKERRQ(ierr);
1114         if (numLabels) {ierr = PetscViewerASCIIPrintf(viewer, "Field %s:\n", name);CHKERRQ(ierr);}
1115         ierr = PetscViewerASCIIPushTab(viewer);CHKERRQ(ierr);
1116         if (dm->fields[f].label) {ierr = DMLabelView(dm->fields[f].label, viewer);CHKERRQ(ierr);}
1117         if (dm->fields[f].adjacency[0]) {
1118           if (dm->fields[f].adjacency[1]) {ierr = PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n");CHKERRQ(ierr);}
1119           else                            {ierr = PetscViewerASCIIPrintf(viewer, "adjacency FVM\n");CHKERRQ(ierr);}
1120         } else {
1121           if (dm->fields[f].adjacency[1]) {ierr = PetscViewerASCIIPrintf(viewer, "adjacency FEM\n");CHKERRQ(ierr);}
1122           else                            {ierr = PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n");CHKERRQ(ierr);}
1123         }
1124         ierr = PetscViewerASCIIPopTab(viewer);CHKERRQ(ierr);
1125       }
1126     }
1127     ierr = DMGetCoarseDM(dm, &cdm);CHKERRQ(ierr);
1128     if (cdm) {
1129       ierr = PetscViewerASCIIPushTab(viewer);CHKERRQ(ierr);
1130       ierr = DMPlexView_Ascii(cdm, viewer);CHKERRQ(ierr);
1131       ierr = PetscViewerASCIIPopTab(viewer);CHKERRQ(ierr);
1132     }
1133   }
1134   PetscFunctionReturn(0);
1135 }
1136 
1137 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1138 {
1139   PetscDraw          draw;
1140   DM                 cdm;
1141   PetscSection       coordSection;
1142   Vec                coordinates;
1143   const PetscScalar *coords;
1144   PetscReal          xyl[2],xyr[2],bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
1145   PetscBool          isnull;
1146   PetscInt           dim, vStart, vEnd, cStart, cEnd, c, N;
1147   PetscMPIInt        rank;
1148   PetscErrorCode     ierr;
1149 
1150   PetscFunctionBegin;
1151   ierr = DMGetCoordinateDim(dm, &dim);CHKERRQ(ierr);
1152   if (dim != 2) SETERRQ1(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %D", dim);
1153   ierr = DMGetCoordinateDM(dm, &cdm);CHKERRQ(ierr);
1154   ierr = DMGetLocalSection(cdm, &coordSection);CHKERRQ(ierr);
1155   ierr = DMGetCoordinatesLocal(dm, &coordinates);CHKERRQ(ierr);
1156   ierr = DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd);CHKERRQ(ierr);
1157   ierr = DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd);CHKERRQ(ierr);
1158 
1159   ierr = PetscViewerDrawGetDraw(viewer, 0, &draw);CHKERRQ(ierr);
1160   ierr = PetscDrawIsNull(draw, &isnull);CHKERRQ(ierr);
1161   if (isnull) PetscFunctionReturn(0);
1162   ierr = PetscDrawSetTitle(draw, "Mesh");CHKERRQ(ierr);
1163 
1164   ierr = VecGetLocalSize(coordinates, &N);CHKERRQ(ierr);
1165   ierr = VecGetArrayRead(coordinates, &coords);CHKERRQ(ierr);
1166   for (c = 0; c < N; c += dim) {
1167     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));   bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
1168     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c+1])); bound[3] = PetscMax(bound[3], PetscRealPart(coords[c+1]));
1169   }
1170   ierr = VecRestoreArrayRead(coordinates, &coords);CHKERRQ(ierr);
1171   ierr = MPIU_Allreduce(&bound[0],xyl,2,MPIU_REAL,MPIU_MIN,PetscObjectComm((PetscObject)dm));CHKERRQ(ierr);
1172   ierr = MPIU_Allreduce(&bound[2],xyr,2,MPIU_REAL,MPIU_MAX,PetscObjectComm((PetscObject)dm));CHKERRQ(ierr);
1173   ierr = PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]);CHKERRQ(ierr);
1174   ierr = PetscDrawClear(draw);CHKERRQ(ierr);
1175 
1176   ierr = MPI_Comm_rank(PetscObjectComm((PetscObject) dm), &rank);CHKERRQ(ierr);
1177   for (c = cStart; c < cEnd; ++c) {
1178     PetscScalar   *coords = NULL;
1179     DMPolytopeType ct;
1180     PetscInt       numCoords;
1181 
1182     ierr = DMPlexGetCellType(dm, c, &ct);CHKERRQ(ierr);
1183     ierr = DMPlexVecGetClosure(dm, coordSection, coordinates, c, &numCoords, &coords);CHKERRQ(ierr);
1184     switch (ct) {
1185     case DM_POLYTOPE_TRIANGLE:
1186       ierr = PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]),
1187                                PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1188                                PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1189                                PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2);CHKERRQ(ierr);
1190       break;
1191     case DM_POLYTOPE_QUADRILATERAL:
1192       ierr = PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]),
1193                                 PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1194                                 PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1195                                 PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2);CHKERRQ(ierr);
1196       ierr = PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]),
1197                                 PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1198                                 PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1199                                 PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2);CHKERRQ(ierr);
1200       break;
1201     default: SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1202     }
1203     ierr = DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords);CHKERRQ(ierr);
1204   }
1205   for (c = cStart; c < cEnd; ++c) {
1206     PetscScalar   *coords = NULL;
1207     DMPolytopeType ct;
1208     PetscInt       numCoords;
1209 
1210     ierr = DMPlexGetCellType(dm, c, &ct);CHKERRQ(ierr);
1211     ierr = DMPlexVecGetClosure(dm, coordSection, coordinates, c, &numCoords, &coords);CHKERRQ(ierr);
1212     switch (ct) {
1213     case DM_POLYTOPE_TRIANGLE:
1214       ierr = PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK);CHKERRQ(ierr);
1215       ierr = PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK);CHKERRQ(ierr);
1216       ierr = PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK);CHKERRQ(ierr);
1217       break;
1218     case DM_POLYTOPE_QUADRILATERAL:
1219       ierr = PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK);CHKERRQ(ierr);
1220       ierr = PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK);CHKERRQ(ierr);
1221       ierr = PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK);CHKERRQ(ierr);
1222       ierr = PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK);CHKERRQ(ierr);
1223       break;
1224     default: SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1225     }
1226     ierr = DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords);CHKERRQ(ierr);
1227   }
1228   ierr = PetscDrawFlush(draw);CHKERRQ(ierr);
1229   ierr = PetscDrawPause(draw);CHKERRQ(ierr);
1230   ierr = PetscDrawSave(draw);CHKERRQ(ierr);
1231   PetscFunctionReturn(0);
1232 }
1233 
1234 #if defined(PETSC_HAVE_EXODUSII)
1235 #include <exodusII.h>
1236 #endif
1237 
1238 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1239 {
1240   PetscBool      iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus;
1241   char           name[PETSC_MAX_PATH_LEN];
1242   PetscErrorCode ierr;
1243 
1244   PetscFunctionBegin;
1245   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1246   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1247   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERASCII,    &iascii);CHKERRQ(ierr);
1248   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,      &isvtk);CHKERRQ(ierr);
1249   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,     &ishdf5);CHKERRQ(ierr);
1250   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERDRAW,     &isdraw);CHKERRQ(ierr);
1251   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERGLVIS,    &isglvis);CHKERRQ(ierr);
1252   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWEREXODUSII, &isexodus);CHKERRQ(ierr);
1253   if (iascii) {
1254     PetscViewerFormat format;
1255     ierr = PetscViewerGetFormat(viewer, &format);CHKERRQ(ierr);
1256     if (format == PETSC_VIEWER_ASCII_GLVIS) {
1257       ierr = DMPlexView_GLVis(dm, viewer);CHKERRQ(ierr);
1258     } else {
1259       ierr = DMPlexView_Ascii(dm, viewer);CHKERRQ(ierr);
1260     }
1261   } else if (ishdf5) {
1262 #if defined(PETSC_HAVE_HDF5)
1263     ierr = DMPlexView_HDF5_Internal(dm, viewer);CHKERRQ(ierr);
1264 #else
1265     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1266 #endif
1267   } else if (isvtk) {
1268     ierr = DMPlexVTKWriteAll((PetscObject) dm,viewer);CHKERRQ(ierr);
1269   } else if (isdraw) {
1270     ierr = DMPlexView_Draw(dm, viewer);CHKERRQ(ierr);
1271   } else if (isglvis) {
1272     ierr = DMPlexView_GLVis(dm, viewer);CHKERRQ(ierr);
1273 #if defined(PETSC_HAVE_EXODUSII)
1274   } else if (isexodus) {
1275     int exoid;
1276     PetscInt cStart, cEnd, c;
1277 
1278     ierr = DMCreateLabel(dm, "Cell Sets");CHKERRQ(ierr);
1279     ierr = DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd);CHKERRQ(ierr);
1280     for (c = cStart; c < cEnd; ++c) {ierr = DMSetLabelValue(dm, "Cell Sets", c, 1);CHKERRQ(ierr);}
1281     ierr = PetscViewerExodusIIGetId(viewer, &exoid);CHKERRQ(ierr);
1282     ierr = DMPlexView_ExodusII_Internal(dm, exoid, 1);CHKERRQ(ierr);
1283 #endif
1284   } else {
1285     SETERRQ1(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1286   }
1287   /* Optionally view the partition */
1288   ierr = PetscOptionsHasName(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_partition_view", &flg);CHKERRQ(ierr);
1289   if (flg) {
1290     Vec ranks;
1291     ierr = DMPlexCreateRankField(dm, &ranks);CHKERRQ(ierr);
1292     ierr = VecView(ranks, viewer);CHKERRQ(ierr);
1293     ierr = VecDestroy(&ranks);CHKERRQ(ierr);
1294   }
1295   /* Optionally view a label */
1296   ierr = PetscOptionsGetString(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_label_view", name, PETSC_MAX_PATH_LEN, &flg);CHKERRQ(ierr);
1297   if (flg) {
1298     DMLabel label;
1299     Vec     val;
1300 
1301     ierr = DMGetLabel(dm, name, &label);CHKERRQ(ierr);
1302     if (!label) SETERRQ1(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1303     ierr = DMPlexCreateLabelField(dm, label, &val);CHKERRQ(ierr);
1304     ierr = VecView(val, viewer);CHKERRQ(ierr);
1305     ierr = VecDestroy(&val);CHKERRQ(ierr);
1306   }
1307   PetscFunctionReturn(0);
1308 }
1309 
1310 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
1311 {
1312   PetscBool      ishdf5;
1313   PetscErrorCode ierr;
1314 
1315   PetscFunctionBegin;
1316   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1317   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1318   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,   &ishdf5);CHKERRQ(ierr);
1319   if (ishdf5) {
1320 #if defined(PETSC_HAVE_HDF5)
1321     PetscViewerFormat format;
1322     ierr = PetscViewerGetFormat(viewer, &format);CHKERRQ(ierr);
1323     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
1324       ierr = DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer);CHKERRQ(ierr);
1325     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1326       ierr = DMPlexLoad_HDF5_Internal(dm, viewer);CHKERRQ(ierr);
1327     } else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1328 #else
1329     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1330 #endif
1331   } else {
1332     SETERRQ1(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
1333   }
1334   PetscFunctionReturn(0);
1335 }
1336 
1337 PetscErrorCode DMDestroy_Plex(DM dm)
1338 {
1339   DM_Plex       *mesh = (DM_Plex*) dm->data;
1340   PetscErrorCode ierr;
1341 
1342   PetscFunctionBegin;
1343   ierr = PetscObjectComposeFunction((PetscObject)dm,"DMSetUpGLVisViewer_C",NULL);CHKERRQ(ierr);
1344   ierr = PetscObjectComposeFunction((PetscObject)dm,"DMPlexInsertBoundaryValues_C", NULL);CHKERRQ(ierr);
1345   ierr = PetscObjectComposeFunction((PetscObject)dm,"DMCreateNeumannOverlap_C", NULL);CHKERRQ(ierr);
1346   if (--mesh->refct > 0) PetscFunctionReturn(0);
1347   ierr = PetscSectionDestroy(&mesh->coneSection);CHKERRQ(ierr);
1348   ierr = PetscFree(mesh->cones);CHKERRQ(ierr);
1349   ierr = PetscFree(mesh->coneOrientations);CHKERRQ(ierr);
1350   ierr = PetscSectionDestroy(&mesh->supportSection);CHKERRQ(ierr);
1351   ierr = PetscSectionDestroy(&mesh->subdomainSection);CHKERRQ(ierr);
1352   ierr = PetscFree(mesh->supports);CHKERRQ(ierr);
1353   ierr = PetscFree(mesh->facesTmp);CHKERRQ(ierr);
1354   ierr = PetscFree(mesh->tetgenOpts);CHKERRQ(ierr);
1355   ierr = PetscFree(mesh->triangleOpts);CHKERRQ(ierr);
1356   ierr = PetscPartitionerDestroy(&mesh->partitioner);CHKERRQ(ierr);
1357   ierr = DMLabelDestroy(&mesh->subpointMap);CHKERRQ(ierr);
1358   ierr = ISDestroy(&mesh->subpointIS);CHKERRQ(ierr);
1359   ierr = ISDestroy(&mesh->globalVertexNumbers);CHKERRQ(ierr);
1360   ierr = ISDestroy(&mesh->globalCellNumbers);CHKERRQ(ierr);
1361   ierr = PetscSectionDestroy(&mesh->anchorSection);CHKERRQ(ierr);
1362   ierr = ISDestroy(&mesh->anchorIS);CHKERRQ(ierr);
1363   ierr = PetscSectionDestroy(&mesh->parentSection);CHKERRQ(ierr);
1364   ierr = PetscFree(mesh->parents);CHKERRQ(ierr);
1365   ierr = PetscFree(mesh->childIDs);CHKERRQ(ierr);
1366   ierr = PetscSectionDestroy(&mesh->childSection);CHKERRQ(ierr);
1367   ierr = PetscFree(mesh->children);CHKERRQ(ierr);
1368   ierr = DMDestroy(&mesh->referenceTree);CHKERRQ(ierr);
1369   ierr = PetscGridHashDestroy(&mesh->lbox);CHKERRQ(ierr);
1370   ierr = PetscFree(mesh->neighbors);CHKERRQ(ierr);
1371   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
1372   ierr = PetscFree(mesh);CHKERRQ(ierr);
1373   PetscFunctionReturn(0);
1374 }
1375 
1376 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
1377 {
1378   PetscSection           sectionGlobal;
1379   PetscInt               bs = -1, mbs;
1380   PetscInt               localSize;
1381   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
1382   PetscErrorCode         ierr;
1383   MatType                mtype;
1384   ISLocalToGlobalMapping ltog;
1385 
1386   PetscFunctionBegin;
1387   ierr = MatInitializePackage();CHKERRQ(ierr);
1388   mtype = dm->mattype;
1389   ierr = DMGetGlobalSection(dm, &sectionGlobal);CHKERRQ(ierr);
1390   /* ierr = PetscSectionGetStorageSize(sectionGlobal, &localSize);CHKERRQ(ierr); */
1391   ierr = PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize);CHKERRQ(ierr);
1392   ierr = MatCreate(PetscObjectComm((PetscObject)dm), J);CHKERRQ(ierr);
1393   ierr = MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE);CHKERRQ(ierr);
1394   ierr = MatSetType(*J, mtype);CHKERRQ(ierr);
1395   ierr = MatSetFromOptions(*J);CHKERRQ(ierr);
1396   ierr = MatGetBlockSize(*J, &mbs);CHKERRQ(ierr);
1397   if (mbs > 1) bs = mbs;
1398   ierr = PetscStrcmp(mtype, MATSHELL, &isShell);CHKERRQ(ierr);
1399   ierr = PetscStrcmp(mtype, MATBAIJ, &isBlock);CHKERRQ(ierr);
1400   ierr = PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock);CHKERRQ(ierr);
1401   ierr = PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock);CHKERRQ(ierr);
1402   ierr = PetscStrcmp(mtype, MATSBAIJ, &isSymBlock);CHKERRQ(ierr);
1403   ierr = PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock);CHKERRQ(ierr);
1404   ierr = PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock);CHKERRQ(ierr);
1405   ierr = PetscStrcmp(mtype, MATIS, &isMatIS);CHKERRQ(ierr);
1406   if (!isShell) {
1407     PetscSection subSection;
1408     PetscBool    fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
1409     PetscInt    *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *ltogidx, lsize;
1410     PetscInt     pStart, pEnd, p, dof, cdof;
1411 
1412     /* Set localtoglobalmapping on the matrix for MatSetValuesLocal() to work (it also creates the local matrices in case of MATIS) */
1413     if (isMatIS) { /* need a different l2g map than the one computed by DMGetLocalToGlobalMapping */
1414       PetscSection section;
1415       PetscInt     size;
1416 
1417       ierr = DMGetLocalSection(dm, &section);CHKERRQ(ierr);
1418       ierr = PetscSectionGetStorageSize(section, &size);CHKERRQ(ierr);
1419       ierr = PetscMalloc1(size,&ltogidx);CHKERRQ(ierr);
1420       ierr = DMPlexGetSubdomainSection(dm, &subSection);CHKERRQ(ierr);
1421     } else {
1422       ierr = DMGetLocalToGlobalMapping(dm,&ltog);CHKERRQ(ierr);
1423     }
1424     ierr = PetscSectionGetChart(sectionGlobal, &pStart, &pEnd);CHKERRQ(ierr);
1425     for (p = pStart, lsize = 0; p < pEnd; ++p) {
1426       PetscInt bdof;
1427 
1428       ierr = PetscSectionGetDof(sectionGlobal, p, &dof);CHKERRQ(ierr);
1429       ierr = PetscSectionGetConstraintDof(sectionGlobal, p, &cdof);CHKERRQ(ierr);
1430       dof  = dof < 0 ? -(dof+1) : dof;
1431       bdof = cdof && (dof-cdof) ? 1 : dof;
1432       if (dof) {
1433         if (bs < 0)          {bs = bdof;}
1434         else if (bs != bdof) {bs = 1; if (!isMatIS) break;}
1435       }
1436       if (isMatIS) {
1437         PetscInt loff,c,off;
1438         ierr = PetscSectionGetOffset(subSection, p, &loff);CHKERRQ(ierr);
1439         ierr = PetscSectionGetOffset(sectionGlobal, p, &off);CHKERRQ(ierr);
1440         for (c = 0; c < dof-cdof; ++c, ++lsize) ltogidx[loff+c] = off > -1 ? off+c : -(off+1)+c;
1441       }
1442     }
1443     /* Must have same blocksize on all procs (some might have no points) */
1444     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs; bsLocal[1] = bs;
1445     ierr = PetscGlobalMinMaxInt(PetscObjectComm((PetscObject) dm), bsLocal, bsMinMax);CHKERRQ(ierr);
1446     if (bsMinMax[0] != bsMinMax[1]) {bs = 1;}
1447     else                            {bs = bsMinMax[0];}
1448     bs = PetscMax(1,bs);
1449     if (isMatIS) { /* Must reduce indices by blocksize */
1450       PetscInt l;
1451 
1452       lsize = lsize/bs;
1453       if (bs > 1) for (l = 0; l < lsize; ++l) ltogidx[l] = ltogidx[l*bs]/bs;
1454       ierr = ISLocalToGlobalMappingCreate(PetscObjectComm((PetscObject)dm), bs, lsize, ltogidx, PETSC_OWN_POINTER, &ltog);CHKERRQ(ierr);
1455     }
1456     ierr = MatSetLocalToGlobalMapping(*J,ltog,ltog);CHKERRQ(ierr);
1457     if (isMatIS) {
1458       ierr = ISLocalToGlobalMappingDestroy(&ltog);CHKERRQ(ierr);
1459     }
1460     ierr = PetscCalloc4(localSize/bs, &dnz, localSize/bs, &onz, localSize/bs, &dnzu, localSize/bs, &onzu);CHKERRQ(ierr);
1461     ierr = DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix);CHKERRQ(ierr);
1462     ierr = PetscFree4(dnz, onz, dnzu, onzu);CHKERRQ(ierr);
1463   }
1464   ierr = MatSetDM(*J, dm);CHKERRQ(ierr);
1465   PetscFunctionReturn(0);
1466 }
1467 
1468 /*@
1469   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
1470 
1471   Not collective
1472 
1473   Input Parameter:
1474 . mesh - The DMPlex
1475 
1476   Output Parameters:
1477 . subsection - The subdomain section
1478 
1479   Level: developer
1480 
1481 .seealso:
1482 @*/
1483 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
1484 {
1485   DM_Plex       *mesh = (DM_Plex*) dm->data;
1486   PetscErrorCode ierr;
1487 
1488   PetscFunctionBegin;
1489   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1490   if (!mesh->subdomainSection) {
1491     PetscSection section;
1492     PetscSF      sf;
1493 
1494     ierr = PetscSFCreate(PETSC_COMM_SELF,&sf);CHKERRQ(ierr);
1495     ierr = DMGetLocalSection(dm,&section);CHKERRQ(ierr);
1496     ierr = PetscSectionCreateGlobalSection(section,sf,PETSC_FALSE,PETSC_TRUE,&mesh->subdomainSection);CHKERRQ(ierr);
1497     ierr = PetscSFDestroy(&sf);CHKERRQ(ierr);
1498   }
1499   *subsection = mesh->subdomainSection;
1500   PetscFunctionReturn(0);
1501 }
1502 
1503 /*@
1504   DMPlexGetChart - Return the interval for all mesh points [pStart, pEnd)
1505 
1506   Not collective
1507 
1508   Input Parameter:
1509 . mesh - The DMPlex
1510 
1511   Output Parameters:
1512 + pStart - The first mesh point
1513 - pEnd   - The upper bound for mesh points
1514 
1515   Level: beginner
1516 
1517 .seealso: DMPlexCreate(), DMPlexSetChart()
1518 @*/
1519 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
1520 {
1521   DM_Plex       *mesh = (DM_Plex*) dm->data;
1522   PetscErrorCode ierr;
1523 
1524   PetscFunctionBegin;
1525   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1526   ierr = PetscSectionGetChart(mesh->coneSection, pStart, pEnd);CHKERRQ(ierr);
1527   PetscFunctionReturn(0);
1528 }
1529 
1530 /*@
1531   DMPlexSetChart - Set the interval for all mesh points [pStart, pEnd)
1532 
1533   Not collective
1534 
1535   Input Parameters:
1536 + mesh - The DMPlex
1537 . pStart - The first mesh point
1538 - pEnd   - The upper bound for mesh points
1539 
1540   Output Parameters:
1541 
1542   Level: beginner
1543 
1544 .seealso: DMPlexCreate(), DMPlexGetChart()
1545 @*/
1546 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
1547 {
1548   DM_Plex       *mesh = (DM_Plex*) dm->data;
1549   PetscErrorCode ierr;
1550 
1551   PetscFunctionBegin;
1552   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1553   ierr = PetscSectionSetChart(mesh->coneSection, pStart, pEnd);CHKERRQ(ierr);
1554   ierr = PetscSectionSetChart(mesh->supportSection, pStart, pEnd);CHKERRQ(ierr);
1555   PetscFunctionReturn(0);
1556 }
1557 
1558 /*@
1559   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
1560 
1561   Not collective
1562 
1563   Input Parameters:
1564 + mesh - The DMPlex
1565 - p - The point, which must lie in the chart set with DMPlexSetChart()
1566 
1567   Output Parameter:
1568 . size - The cone size for point p
1569 
1570   Level: beginner
1571 
1572 .seealso: DMPlexCreate(), DMPlexSetConeSize(), DMPlexSetChart()
1573 @*/
1574 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
1575 {
1576   DM_Plex       *mesh = (DM_Plex*) dm->data;
1577   PetscErrorCode ierr;
1578 
1579   PetscFunctionBegin;
1580   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1581   PetscValidPointer(size, 3);
1582   ierr = PetscSectionGetDof(mesh->coneSection, p, size);CHKERRQ(ierr);
1583   PetscFunctionReturn(0);
1584 }
1585 
1586 /*@
1587   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
1588 
1589   Not collective
1590 
1591   Input Parameters:
1592 + mesh - The DMPlex
1593 . p - The point, which must lie in the chart set with DMPlexSetChart()
1594 - size - The cone size for point p
1595 
1596   Output Parameter:
1597 
1598   Note:
1599   This should be called after DMPlexSetChart().
1600 
1601   Level: beginner
1602 
1603 .seealso: DMPlexCreate(), DMPlexGetConeSize(), DMPlexSetChart()
1604 @*/
1605 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
1606 {
1607   DM_Plex       *mesh = (DM_Plex*) dm->data;
1608   PetscErrorCode ierr;
1609 
1610   PetscFunctionBegin;
1611   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1612   ierr = PetscSectionSetDof(mesh->coneSection, p, size);CHKERRQ(ierr);
1613 
1614   mesh->maxConeSize = PetscMax(mesh->maxConeSize, size);
1615   PetscFunctionReturn(0);
1616 }
1617 
1618 /*@
1619   DMPlexAddConeSize - Add the given number of in-edges to this point in the DAG
1620 
1621   Not collective
1622 
1623   Input Parameters:
1624 + mesh - The DMPlex
1625 . p - The point, which must lie in the chart set with DMPlexSetChart()
1626 - size - The additional cone size for point p
1627 
1628   Output Parameter:
1629 
1630   Note:
1631   This should be called after DMPlexSetChart().
1632 
1633   Level: beginner
1634 
1635 .seealso: DMPlexCreate(), DMPlexSetConeSize(), DMPlexGetConeSize(), DMPlexSetChart()
1636 @*/
1637 PetscErrorCode DMPlexAddConeSize(DM dm, PetscInt p, PetscInt size)
1638 {
1639   DM_Plex       *mesh = (DM_Plex*) dm->data;
1640   PetscInt       csize;
1641   PetscErrorCode ierr;
1642 
1643   PetscFunctionBegin;
1644   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1645   ierr = PetscSectionAddDof(mesh->coneSection, p, size);CHKERRQ(ierr);
1646   ierr = PetscSectionGetDof(mesh->coneSection, p, &csize);CHKERRQ(ierr);
1647 
1648   mesh->maxConeSize = PetscMax(mesh->maxConeSize, csize);
1649   PetscFunctionReturn(0);
1650 }
1651 
1652 /*@C
1653   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
1654 
1655   Not collective
1656 
1657   Input Parameters:
1658 + dm - The DMPlex
1659 - p - The point, which must lie in the chart set with DMPlexSetChart()
1660 
1661   Output Parameter:
1662 . cone - An array of points which are on the in-edges for point p
1663 
1664   Level: beginner
1665 
1666   Fortran Notes:
1667   Since it returns an array, this routine is only available in Fortran 90, and you must
1668   include petsc.h90 in your code.
1669   You must also call DMPlexRestoreCone() after you finish using the returned array.
1670   DMPlexRestoreCone() is not needed/available in C.
1671 
1672 .seealso: DMPlexCreate(), DMPlexSetCone(), DMPlexGetConeTuple(), DMPlexSetChart()
1673 @*/
1674 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
1675 {
1676   DM_Plex       *mesh = (DM_Plex*) dm->data;
1677   PetscInt       off;
1678   PetscErrorCode ierr;
1679 
1680   PetscFunctionBegin;
1681   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1682   PetscValidPointer(cone, 3);
1683   ierr  = PetscSectionGetOffset(mesh->coneSection, p, &off);CHKERRQ(ierr);
1684   *cone = &mesh->cones[off];
1685   PetscFunctionReturn(0);
1686 }
1687 
1688 /*@C
1689   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
1690 
1691   Not collective
1692 
1693   Input Parameters:
1694 + dm - The DMPlex
1695 - p - The IS of points, which must lie in the chart set with DMPlexSetChart()
1696 
1697   Output Parameter:
1698 + pConesSection - PetscSection describing the layout of pCones
1699 - pCones - An array of points which are on the in-edges for the point set p
1700 
1701   Level: intermediate
1702 
1703 .seealso: DMPlexCreate(), DMPlexGetCone(), DMPlexGetConeRecursive(), DMPlexSetChart()
1704 @*/
1705 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
1706 {
1707   PetscSection        cs, newcs;
1708   PetscInt            *cones;
1709   PetscInt            *newarr=NULL;
1710   PetscInt            n;
1711   PetscErrorCode      ierr;
1712 
1713   PetscFunctionBegin;
1714   ierr = DMPlexGetCones(dm, &cones);CHKERRQ(ierr);
1715   ierr = DMPlexGetConeSection(dm, &cs);CHKERRQ(ierr);
1716   ierr = PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void**)&newarr) : NULL);CHKERRQ(ierr);
1717   if (pConesSection) *pConesSection = newcs;
1718   if (pCones) {
1719     ierr = PetscSectionGetStorageSize(newcs, &n);CHKERRQ(ierr);
1720     ierr = ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones);CHKERRQ(ierr);
1721   }
1722   PetscFunctionReturn(0);
1723 }
1724 
1725 /*@
1726   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
1727 
1728   Not collective
1729 
1730   Input Parameters:
1731 + dm - The DMPlex
1732 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
1733 
1734   Output Parameter:
1735 . expandedPoints - An array of vertices recursively expanded from input points
1736 
1737   Level: advanced
1738 
1739   Notes:
1740   Like DMPlexGetConeRecursive but returns only the 0-depth IS (i.e. vertices only) and no sections.
1741   There is no corresponding Restore function, just call ISDestroy() on the returned IS to deallocate.
1742 
1743 .seealso: DMPlexCreate(), DMPlexGetCone(), DMPlexGetConeTuple(), DMPlexGetConeRecursive(), DMPlexRestoreConeRecursive(), DMPlexGetDepth()
1744 @*/
1745 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
1746 {
1747   IS                  *expandedPointsAll;
1748   PetscInt            depth;
1749   PetscErrorCode      ierr;
1750 
1751   PetscFunctionBegin;
1752   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1753   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
1754   PetscValidPointer(expandedPoints, 3);
1755   ierr = DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL);CHKERRQ(ierr);
1756   *expandedPoints = expandedPointsAll[0];
1757   ierr = PetscObjectReference((PetscObject)expandedPointsAll[0]);
1758   ierr = DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL);CHKERRQ(ierr);
1759   PetscFunctionReturn(0);
1760 }
1761 
1762 /*@
1763   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).
1764 
1765   Not collective
1766 
1767   Input Parameters:
1768 + dm - The DMPlex
1769 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
1770 
1771   Output Parameter:
1772 + depth - (optional) Size of the output arrays, equal to DMPlex depth, returned by DMPlexGetDepth()
1773 . expandedPoints - (optional) An array of index sets with recursively expanded cones
1774 - sections - (optional) An array of sections which describe mappings from points to their cone points
1775 
1776   Level: advanced
1777 
1778   Notes:
1779   Like DMPlexGetConeTuple() but recursive.
1780 
1781   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.
1782   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
1783 
1784   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:
1785   (1) DAG points in expandedPoints[d+1] with depth d+1 to their cone points in expandedPoints[d];
1786   (2) DAG points in expandedPoints[d+1] with depth in [0,d] to the same points in expandedPoints[d].
1787 
1788 .seealso: DMPlexCreate(), DMPlexGetCone(), DMPlexGetConeTuple(), DMPlexRestoreConeRecursive(), DMPlexGetConeRecursiveVertices(), DMPlexGetDepth()
1789 @*/
1790 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
1791 {
1792   const PetscInt      *arr0=NULL, *cone=NULL;
1793   PetscInt            *arr=NULL, *newarr=NULL;
1794   PetscInt            d, depth_, i, n, newn, cn, co, start, end;
1795   IS                  *expandedPoints_;
1796   PetscSection        *sections_;
1797   PetscErrorCode      ierr;
1798 
1799   PetscFunctionBegin;
1800   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1801   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
1802   if (depth) PetscValidIntPointer(depth, 3);
1803   if (expandedPoints) PetscValidPointer(expandedPoints, 4);
1804   if (sections) PetscValidPointer(sections, 5);
1805   ierr = ISGetLocalSize(points, &n);CHKERRQ(ierr);
1806   ierr = ISGetIndices(points, &arr0);CHKERRQ(ierr);
1807   ierr = DMPlexGetDepth(dm, &depth_);CHKERRQ(ierr);
1808   ierr = PetscCalloc1(depth_, &expandedPoints_);CHKERRQ(ierr);
1809   ierr = PetscCalloc1(depth_, &sections_);CHKERRQ(ierr);
1810   arr = (PetscInt*) arr0; /* this is ok because first generation of arr is not modified */
1811   for (d=depth_-1; d>=0; d--) {
1812     ierr = PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]);CHKERRQ(ierr);
1813     ierr = PetscSectionSetChart(sections_[d], 0, n);CHKERRQ(ierr);
1814     for (i=0; i<n; i++) {
1815       ierr = DMPlexGetDepthStratum(dm, d+1, &start, &end);CHKERRQ(ierr);
1816       if (arr[i] >= start && arr[i] < end) {
1817         ierr = DMPlexGetConeSize(dm, arr[i], &cn);CHKERRQ(ierr);
1818         ierr = PetscSectionSetDof(sections_[d], i, cn);CHKERRQ(ierr);
1819       } else {
1820         ierr = PetscSectionSetDof(sections_[d], i, 1);CHKERRQ(ierr);
1821       }
1822     }
1823     ierr = PetscSectionSetUp(sections_[d]);CHKERRQ(ierr);
1824     ierr = PetscSectionGetStorageSize(sections_[d], &newn);CHKERRQ(ierr);
1825     ierr = PetscMalloc1(newn, &newarr);CHKERRQ(ierr);
1826     for (i=0; i<n; i++) {
1827       ierr = PetscSectionGetDof(sections_[d], i, &cn);CHKERRQ(ierr);
1828       ierr = PetscSectionGetOffset(sections_[d], i, &co);CHKERRQ(ierr);
1829       if (cn > 1) {
1830         ierr = DMPlexGetCone(dm, arr[i], &cone);CHKERRQ(ierr);
1831         ierr = PetscMemcpy(&newarr[co], cone, cn*sizeof(PetscInt));CHKERRQ(ierr);
1832       } else {
1833         newarr[co] = arr[i];
1834       }
1835     }
1836     ierr = ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]);CHKERRQ(ierr);
1837     arr = newarr;
1838     n = newn;
1839   }
1840   ierr = ISRestoreIndices(points, &arr0);CHKERRQ(ierr);
1841   *depth = depth_;
1842   if (expandedPoints) *expandedPoints = expandedPoints_;
1843   else {
1844     for (d=0; d<depth_; d++) {ierr = ISDestroy(&expandedPoints_[d]);CHKERRQ(ierr);}
1845     ierr = PetscFree(expandedPoints_);CHKERRQ(ierr);
1846   }
1847   if (sections) *sections = sections_;
1848   else {
1849     for (d=0; d<depth_; d++) {ierr = PetscSectionDestroy(&sections_[d]);CHKERRQ(ierr);}
1850     ierr = PetscFree(sections_);CHKERRQ(ierr);
1851   }
1852   PetscFunctionReturn(0);
1853 }
1854 
1855 /*@
1856   DMPlexRestoreConeRecursive - Deallocates arrays created by DMPlexGetConeRecursive
1857 
1858   Not collective
1859 
1860   Input Parameters:
1861 + dm - The DMPlex
1862 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
1863 
1864   Output Parameter:
1865 + depth - (optional) Size of the output arrays, equal to DMPlex depth, returned by DMPlexGetDepth()
1866 . expandedPoints - (optional) An array of recursively expanded cones
1867 - sections - (optional) An array of sections which describe mappings from points to their cone points
1868 
1869   Level: advanced
1870 
1871   Notes:
1872   See DMPlexGetConeRecursive() for details.
1873 
1874 .seealso: DMPlexCreate(), DMPlexGetCone(), DMPlexGetConeTuple(), DMPlexGetConeRecursive(), DMPlexGetConeRecursiveVertices(), DMPlexGetDepth()
1875 @*/
1876 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
1877 {
1878   PetscInt            d, depth_;
1879   PetscErrorCode      ierr;
1880 
1881   PetscFunctionBegin;
1882   ierr = DMPlexGetDepth(dm, &depth_);CHKERRQ(ierr);
1883   if (depth && *depth != depth_) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
1884   if (depth) *depth = 0;
1885   if (expandedPoints) {
1886     for (d=0; d<depth_; d++) {ierr = ISDestroy(&((*expandedPoints)[d]));CHKERRQ(ierr);}
1887     ierr = PetscFree(*expandedPoints);CHKERRQ(ierr);
1888   }
1889   if (sections)  {
1890     for (d=0; d<depth_; d++) {ierr = PetscSectionDestroy(&((*sections)[d]));CHKERRQ(ierr);}
1891     ierr = PetscFree(*sections);CHKERRQ(ierr);
1892   }
1893   PetscFunctionReturn(0);
1894 }
1895 
1896 /*@
1897   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
1898 
1899   Not collective
1900 
1901   Input Parameters:
1902 + mesh - The DMPlex
1903 . p - The point, which must lie in the chart set with DMPlexSetChart()
1904 - cone - An array of points which are on the in-edges for point p
1905 
1906   Output Parameter:
1907 
1908   Note:
1909   This should be called after all calls to DMPlexSetConeSize() and DMSetUp().
1910 
1911   Developer Note: Why not call this DMPlexSetCover()
1912 
1913   Level: beginner
1914 
1915 .seealso: DMPlexCreate(), DMPlexGetCone(), DMPlexSetChart(), DMPlexSetConeSize(), DMSetUp(), DMPlexSetSupport(), DMPlexSetSupportSize()
1916 @*/
1917 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
1918 {
1919   DM_Plex       *mesh = (DM_Plex*) dm->data;
1920   PetscInt       pStart, pEnd;
1921   PetscInt       dof, off, c;
1922   PetscErrorCode ierr;
1923 
1924   PetscFunctionBegin;
1925   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1926   ierr = PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd);CHKERRQ(ierr);
1927   ierr = PetscSectionGetDof(mesh->coneSection, p, &dof);CHKERRQ(ierr);
1928   if (dof) PetscValidPointer(cone, 3);
1929   ierr = PetscSectionGetOffset(mesh->coneSection, p, &off);CHKERRQ(ierr);
1930   if ((p < pStart) || (p >= pEnd)) SETERRQ3(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %D is not in the valid range [%D, %D)", p, pStart, pEnd);
1931   for (c = 0; c < dof; ++c) {
1932     if ((cone[c] < pStart) || (cone[c] >= pEnd)) SETERRQ3(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone point %D is not in the valid range [%D, %D)", cone[c], pStart, pEnd);
1933     mesh->cones[off+c] = cone[c];
1934   }
1935   PetscFunctionReturn(0);
1936 }
1937 
1938 /*@C
1939   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
1940 
1941   Not collective
1942 
1943   Input Parameters:
1944 + mesh - The DMPlex
1945 - p - The point, which must lie in the chart set with DMPlexSetChart()
1946 
1947   Output Parameter:
1948 . coneOrientation - An array of orientations which are on the in-edges for point p. An orientation is an
1949                     integer giving the prescription for cone traversal. If it is negative, the cone is
1950                     traversed in the opposite direction. Its value 'o', or if negative '-(o+1)', gives
1951                     the index of the cone point on which to start.
1952 
1953   Level: beginner
1954 
1955   Fortran Notes:
1956   Since it returns an array, this routine is only available in Fortran 90, and you must
1957   include petsc.h90 in your code.
1958   You must also call DMPlexRestoreConeOrientation() after you finish using the returned array.
1959   DMPlexRestoreConeOrientation() is not needed/available in C.
1960 
1961 .seealso: DMPlexCreate(), DMPlexGetCone(), DMPlexSetCone(), DMPlexSetChart()
1962 @*/
1963 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
1964 {
1965   DM_Plex       *mesh = (DM_Plex*) dm->data;
1966   PetscInt       off;
1967   PetscErrorCode ierr;
1968 
1969   PetscFunctionBegin;
1970   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1971   if (PetscDefined(USE_DEBUG)) {
1972     PetscInt dof;
1973     ierr = PetscSectionGetDof(mesh->coneSection, p, &dof);CHKERRQ(ierr);
1974     if (dof) PetscValidPointer(coneOrientation, 3);
1975   }
1976   ierr = PetscSectionGetOffset(mesh->coneSection, p, &off);CHKERRQ(ierr);
1977 
1978   *coneOrientation = &mesh->coneOrientations[off];
1979   PetscFunctionReturn(0);
1980 }
1981 
1982 /*@
1983   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
1984 
1985   Not collective
1986 
1987   Input Parameters:
1988 + mesh - The DMPlex
1989 . p - The point, which must lie in the chart set with DMPlexSetChart()
1990 - coneOrientation - An array of orientations which are on the in-edges for point p. An orientation is an
1991                     integer giving the prescription for cone traversal. If it is negative, the cone is
1992                     traversed in the opposite direction. Its value 'o', or if negative '-(o+1)', gives
1993                     the index of the cone point on which to start.
1994 
1995   Output Parameter:
1996 
1997   Note:
1998   This should be called after all calls to DMPlexSetConeSize() and DMSetUp().
1999 
2000   Level: beginner
2001 
2002 .seealso: DMPlexCreate(), DMPlexGetConeOrientation(), DMPlexSetCone(), DMPlexSetChart(), DMPlexSetConeSize(), DMSetUp()
2003 @*/
2004 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
2005 {
2006   DM_Plex       *mesh = (DM_Plex*) dm->data;
2007   PetscInt       pStart, pEnd;
2008   PetscInt       dof, off, c;
2009   PetscErrorCode ierr;
2010 
2011   PetscFunctionBegin;
2012   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2013   ierr = PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd);CHKERRQ(ierr);
2014   ierr = PetscSectionGetDof(mesh->coneSection, p, &dof);CHKERRQ(ierr);
2015   if (dof) PetscValidPointer(coneOrientation, 3);
2016   ierr = PetscSectionGetOffset(mesh->coneSection, p, &off);CHKERRQ(ierr);
2017   if ((p < pStart) || (p >= pEnd)) SETERRQ3(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %D is not in the valid range [%D, %D)", p, pStart, pEnd);
2018   for (c = 0; c < dof; ++c) {
2019     PetscInt cdof, o = coneOrientation[c];
2020 
2021     ierr = PetscSectionGetDof(mesh->coneSection, mesh->cones[off+c], &cdof);CHKERRQ(ierr);
2022     if (o && ((o < -(cdof+1)) || (o >= cdof))) SETERRQ3(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone orientation %D is not in the valid range [%D. %D)", o, -(cdof+1), cdof);
2023     mesh->coneOrientations[off+c] = o;
2024   }
2025   PetscFunctionReturn(0);
2026 }
2027 
2028 /*@
2029   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
2030 
2031   Not collective
2032 
2033   Input Parameters:
2034 + mesh - The DMPlex
2035 . p - The point, which must lie in the chart set with DMPlexSetChart()
2036 . conePos - The local index in the cone where the point should be put
2037 - conePoint - The mesh point to insert
2038 
2039   Level: beginner
2040 
2041 .seealso: DMPlexCreate(), DMPlexGetCone(), DMPlexSetChart(), DMPlexSetConeSize(), DMSetUp()
2042 @*/
2043 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
2044 {
2045   DM_Plex       *mesh = (DM_Plex*) dm->data;
2046   PetscInt       pStart, pEnd;
2047   PetscInt       dof, off;
2048   PetscErrorCode ierr;
2049 
2050   PetscFunctionBegin;
2051   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2052   ierr = PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd);CHKERRQ(ierr);
2053   if ((p < pStart) || (p >= pEnd)) SETERRQ3(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %D is not in the valid range [%D, %D)", p, pStart, pEnd);
2054   if ((conePoint < pStart) || (conePoint >= pEnd)) SETERRQ3(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone point %D is not in the valid range [%D, %D)", conePoint, pStart, pEnd);
2055   ierr = PetscSectionGetDof(mesh->coneSection, p, &dof);CHKERRQ(ierr);
2056   ierr = PetscSectionGetOffset(mesh->coneSection, p, &off);CHKERRQ(ierr);
2057   if ((conePos < 0) || (conePos >= dof)) SETERRQ3(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone position %D of point %D is not in the valid range [0, %D)", conePos, p, dof);
2058   mesh->cones[off+conePos] = conePoint;
2059   PetscFunctionReturn(0);
2060 }
2061 
2062 /*@
2063   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
2064 
2065   Not collective
2066 
2067   Input Parameters:
2068 + mesh - The DMPlex
2069 . p - The point, which must lie in the chart set with DMPlexSetChart()
2070 . conePos - The local index in the cone where the point should be put
2071 - coneOrientation - The point orientation to insert
2072 
2073   Level: beginner
2074 
2075 .seealso: DMPlexCreate(), DMPlexGetCone(), DMPlexSetChart(), DMPlexSetConeSize(), DMSetUp()
2076 @*/
2077 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
2078 {
2079   DM_Plex       *mesh = (DM_Plex*) dm->data;
2080   PetscInt       pStart, pEnd;
2081   PetscInt       dof, off;
2082   PetscErrorCode ierr;
2083 
2084   PetscFunctionBegin;
2085   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2086   ierr = PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd);CHKERRQ(ierr);
2087   if ((p < pStart) || (p >= pEnd)) SETERRQ3(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %D is not in the valid range [%D, %D)", p, pStart, pEnd);
2088   ierr = PetscSectionGetDof(mesh->coneSection, p, &dof);CHKERRQ(ierr);
2089   ierr = PetscSectionGetOffset(mesh->coneSection, p, &off);CHKERRQ(ierr);
2090   if ((conePos < 0) || (conePos >= dof)) SETERRQ3(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone position %D of point %D is not in the valid range [0, %D)", conePos, p, dof);
2091   mesh->coneOrientations[off+conePos] = coneOrientation;
2092   PetscFunctionReturn(0);
2093 }
2094 
2095 /*@
2096   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
2097 
2098   Not collective
2099 
2100   Input Parameters:
2101 + mesh - The DMPlex
2102 - p - The point, which must lie in the chart set with DMPlexSetChart()
2103 
2104   Output Parameter:
2105 . size - The support size for point p
2106 
2107   Level: beginner
2108 
2109 .seealso: DMPlexCreate(), DMPlexSetConeSize(), DMPlexSetChart(), DMPlexGetConeSize()
2110 @*/
2111 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
2112 {
2113   DM_Plex       *mesh = (DM_Plex*) dm->data;
2114   PetscErrorCode ierr;
2115 
2116   PetscFunctionBegin;
2117   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2118   PetscValidPointer(size, 3);
2119   ierr = PetscSectionGetDof(mesh->supportSection, p, size);CHKERRQ(ierr);
2120   PetscFunctionReturn(0);
2121 }
2122 
2123 /*@
2124   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
2125 
2126   Not collective
2127 
2128   Input Parameters:
2129 + mesh - The DMPlex
2130 . p - The point, which must lie in the chart set with DMPlexSetChart()
2131 - size - The support size for point p
2132 
2133   Output Parameter:
2134 
2135   Note:
2136   This should be called after DMPlexSetChart().
2137 
2138   Level: beginner
2139 
2140 .seealso: DMPlexCreate(), DMPlexGetSupportSize(), DMPlexSetChart()
2141 @*/
2142 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
2143 {
2144   DM_Plex       *mesh = (DM_Plex*) dm->data;
2145   PetscErrorCode ierr;
2146 
2147   PetscFunctionBegin;
2148   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2149   ierr = PetscSectionSetDof(mesh->supportSection, p, size);CHKERRQ(ierr);
2150 
2151   mesh->maxSupportSize = PetscMax(mesh->maxSupportSize, size);
2152   PetscFunctionReturn(0);
2153 }
2154 
2155 /*@C
2156   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
2157 
2158   Not collective
2159 
2160   Input Parameters:
2161 + mesh - The DMPlex
2162 - p - The point, which must lie in the chart set with DMPlexSetChart()
2163 
2164   Output Parameter:
2165 . support - An array of points which are on the out-edges for point p
2166 
2167   Level: beginner
2168 
2169   Fortran Notes:
2170   Since it returns an array, this routine is only available in Fortran 90, and you must
2171   include petsc.h90 in your code.
2172   You must also call DMPlexRestoreSupport() after you finish using the returned array.
2173   DMPlexRestoreSupport() is not needed/available in C.
2174 
2175 .seealso: DMPlexCreate(), DMPlexSetCone(), DMPlexSetChart(), DMPlexGetCone()
2176 @*/
2177 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
2178 {
2179   DM_Plex       *mesh = (DM_Plex*) dm->data;
2180   PetscInt       off;
2181   PetscErrorCode ierr;
2182 
2183   PetscFunctionBegin;
2184   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2185   PetscValidPointer(support, 3);
2186   ierr     = PetscSectionGetOffset(mesh->supportSection, p, &off);CHKERRQ(ierr);
2187   *support = &mesh->supports[off];
2188   PetscFunctionReturn(0);
2189 }
2190 
2191 /*@
2192   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
2193 
2194   Not collective
2195 
2196   Input Parameters:
2197 + mesh - The DMPlex
2198 . p - The point, which must lie in the chart set with DMPlexSetChart()
2199 - support - An array of points which are on the out-edges for point p
2200 
2201   Output Parameter:
2202 
2203   Note:
2204   This should be called after all calls to DMPlexSetSupportSize() and DMSetUp().
2205 
2206   Level: beginner
2207 
2208 .seealso: DMPlexSetCone(), DMPlexSetConeSize(), DMPlexCreate(), DMPlexGetSupport(), DMPlexSetChart(), DMPlexSetSupportSize(), DMSetUp()
2209 @*/
2210 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
2211 {
2212   DM_Plex       *mesh = (DM_Plex*) dm->data;
2213   PetscInt       pStart, pEnd;
2214   PetscInt       dof, off, c;
2215   PetscErrorCode ierr;
2216 
2217   PetscFunctionBegin;
2218   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2219   ierr = PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd);CHKERRQ(ierr);
2220   ierr = PetscSectionGetDof(mesh->supportSection, p, &dof);CHKERRQ(ierr);
2221   if (dof) PetscValidPointer(support, 3);
2222   ierr = PetscSectionGetOffset(mesh->supportSection, p, &off);CHKERRQ(ierr);
2223   if ((p < pStart) || (p >= pEnd)) SETERRQ3(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %D is not in the valid range [%D, %D)", p, pStart, pEnd);
2224   for (c = 0; c < dof; ++c) {
2225     if ((support[c] < pStart) || (support[c] >= pEnd)) SETERRQ3(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Support point %D is not in the valid range [%D, %D)", support[c], pStart, pEnd);
2226     mesh->supports[off+c] = support[c];
2227   }
2228   PetscFunctionReturn(0);
2229 }
2230 
2231 /*@
2232   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
2233 
2234   Not collective
2235 
2236   Input Parameters:
2237 + mesh - The DMPlex
2238 . p - The point, which must lie in the chart set with DMPlexSetChart()
2239 . supportPos - The local index in the cone where the point should be put
2240 - supportPoint - The mesh point to insert
2241 
2242   Level: beginner
2243 
2244 .seealso: DMPlexCreate(), DMPlexGetCone(), DMPlexSetChart(), DMPlexSetConeSize(), DMSetUp()
2245 @*/
2246 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
2247 {
2248   DM_Plex       *mesh = (DM_Plex*) dm->data;
2249   PetscInt       pStart, pEnd;
2250   PetscInt       dof, off;
2251   PetscErrorCode ierr;
2252 
2253   PetscFunctionBegin;
2254   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2255   ierr = PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd);CHKERRQ(ierr);
2256   ierr = PetscSectionGetDof(mesh->supportSection, p, &dof);CHKERRQ(ierr);
2257   ierr = PetscSectionGetOffset(mesh->supportSection, p, &off);CHKERRQ(ierr);
2258   if ((p < pStart) || (p >= pEnd)) SETERRQ3(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %D is not in the valid range [%D, %D)", p, pStart, pEnd);
2259   if ((supportPoint < pStart) || (supportPoint >= pEnd)) SETERRQ3(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Support point %D is not in the valid range [%D, %D)", supportPoint, pStart, pEnd);
2260   if (supportPos >= dof) SETERRQ3(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Support position %D of point %D is not in the valid range [0, %D)", supportPos, p, dof);
2261   mesh->supports[off+supportPos] = supportPoint;
2262   PetscFunctionReturn(0);
2263 }
2264 
2265 /*@C
2266   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
2267 
2268   Not collective
2269 
2270   Input Parameters:
2271 + mesh - The DMPlex
2272 . p - The point, which must lie in the chart set with DMPlexSetChart()
2273 . useCone - PETSC_TRUE for in-edges,  otherwise use out-edges
2274 - points - If points is NULL on input, internal storage will be returned, otherwise the provided array is used
2275 
2276   Output Parameters:
2277 + numPoints - The number of points in the closure, so points[] is of size 2*numPoints
2278 - points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
2279 
2280   Note:
2281   If using internal storage (points is NULL on input), each call overwrites the last output.
2282 
2283   Fortran Notes:
2284   Since it returns an array, this routine is only available in Fortran 90, and you must
2285   include petsc.h90 in your code.
2286 
2287   The numPoints argument is not present in the Fortran 90 binding since it is internal to the array.
2288 
2289   Level: beginner
2290 
2291 .seealso: DMPlexRestoreTransitiveClosure(), DMPlexCreate(), DMPlexSetCone(), DMPlexSetChart(), DMPlexGetCone()
2292 @*/
2293 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
2294 {
2295   DM_Plex        *mesh = (DM_Plex*) dm->data;
2296   PetscInt       *closure, *fifo;
2297   const PetscInt *tmp = NULL, *tmpO = NULL;
2298   PetscInt        tmpSize, t;
2299   PetscInt        depth       = 0, maxSize;
2300   PetscInt        closureSize = 2, fifoSize = 0, fifoStart = 0;
2301   PetscErrorCode  ierr;
2302 
2303   PetscFunctionBegin;
2304   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2305   ierr    = DMPlexGetDepth(dm, &depth);CHKERRQ(ierr);
2306   /* This is only 1-level */
2307   if (useCone) {
2308     ierr = DMPlexGetConeSize(dm, p, &tmpSize);CHKERRQ(ierr);
2309     ierr = DMPlexGetCone(dm, p, &tmp);CHKERRQ(ierr);
2310     ierr = DMPlexGetConeOrientation(dm, p, &tmpO);CHKERRQ(ierr);
2311   } else {
2312     ierr = DMPlexGetSupportSize(dm, p, &tmpSize);CHKERRQ(ierr);
2313     ierr = DMPlexGetSupport(dm, p, &tmp);CHKERRQ(ierr);
2314   }
2315   if (depth == 1) {
2316     if (*points) {
2317       closure = *points;
2318     } else {
2319       maxSize = 2*(PetscMax(mesh->maxConeSize, mesh->maxSupportSize)+1);
2320       ierr = DMGetWorkArray(dm, maxSize, MPIU_INT, &closure);CHKERRQ(ierr);
2321     }
2322     closure[0] = p; closure[1] = 0;
2323     for (t = 0; t < tmpSize; ++t, closureSize += 2) {
2324       closure[closureSize]   = tmp[t];
2325       closure[closureSize+1] = tmpO ? tmpO[t] : 0;
2326     }
2327     if (numPoints) *numPoints = closureSize/2;
2328     if (points)    *points    = closure;
2329     PetscFunctionReturn(0);
2330   }
2331   {
2332     PetscInt c, coneSeries, s,supportSeries;
2333 
2334     c = mesh->maxConeSize;
2335     coneSeries = (c > 1) ? ((PetscPowInt(c,depth+1)-1)/(c-1)) : depth+1;
2336     s = mesh->maxSupportSize;
2337     supportSeries = (s > 1) ? ((PetscPowInt(s,depth+1)-1)/(s-1)) : depth+1;
2338     maxSize = 2*PetscMax(coneSeries,supportSeries);
2339   }
2340   ierr    = DMGetWorkArray(dm, maxSize, MPIU_INT, &fifo);CHKERRQ(ierr);
2341   if (*points) {
2342     closure = *points;
2343   } else {
2344     ierr = DMGetWorkArray(dm, maxSize, MPIU_INT, &closure);CHKERRQ(ierr);
2345   }
2346   closure[0] = p; closure[1] = 0;
2347   for (t = 0; t < tmpSize; ++t, closureSize += 2, fifoSize += 2) {
2348     const PetscInt cp = tmp[t];
2349     const PetscInt co = tmpO ? tmpO[t] : 0;
2350 
2351     closure[closureSize]   = cp;
2352     closure[closureSize+1] = co;
2353     fifo[fifoSize]         = cp;
2354     fifo[fifoSize+1]       = co;
2355   }
2356   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
2357   while (fifoSize - fifoStart) {
2358     const PetscInt q   = fifo[fifoStart];
2359     const PetscInt o   = fifo[fifoStart+1];
2360     const PetscInt rev = o >= 0 ? 0 : 1;
2361     const PetscInt off = rev ? -(o+1) : o;
2362 
2363     if (useCone) {
2364       ierr = DMPlexGetConeSize(dm, q, &tmpSize);CHKERRQ(ierr);
2365       ierr = DMPlexGetCone(dm, q, &tmp);CHKERRQ(ierr);
2366       ierr = DMPlexGetConeOrientation(dm, q, &tmpO);CHKERRQ(ierr);
2367     } else {
2368       ierr = DMPlexGetSupportSize(dm, q, &tmpSize);CHKERRQ(ierr);
2369       ierr = DMPlexGetSupport(dm, q, &tmp);CHKERRQ(ierr);
2370       tmpO = NULL;
2371     }
2372     for (t = 0; t < tmpSize; ++t) {
2373       const PetscInt i  = ((rev ? tmpSize-t : t) + off)%tmpSize;
2374       const PetscInt cp = tmp[i];
2375       /* Must propogate orientation: When we reverse orientation, we both reverse the direction of iteration and start at the other end of the chain. */
2376       /* HACK: It is worse to get the size here, than to change the interpretation of -(*+1)
2377        const PetscInt co = tmpO ? (rev ? -(tmpO[i]+1) : tmpO[i]) : 0; */
2378       PetscInt       co = tmpO ? tmpO[i] : 0;
2379       PetscInt       c;
2380 
2381       if (rev) {
2382         PetscInt childSize, coff;
2383         ierr = DMPlexGetConeSize(dm, cp, &childSize);CHKERRQ(ierr);
2384         coff = tmpO[i] < 0 ? -(tmpO[i]+1) : tmpO[i];
2385         co   = childSize ? -(((coff+childSize-1)%childSize)+1) : 0;
2386       }
2387       /* Check for duplicate */
2388       for (c = 0; c < closureSize; c += 2) {
2389         if (closure[c] == cp) break;
2390       }
2391       if (c == closureSize) {
2392         closure[closureSize]   = cp;
2393         closure[closureSize+1] = co;
2394         fifo[fifoSize]         = cp;
2395         fifo[fifoSize+1]       = co;
2396         closureSize           += 2;
2397         fifoSize              += 2;
2398       }
2399     }
2400     fifoStart += 2;
2401   }
2402   if (numPoints) *numPoints = closureSize/2;
2403   if (points)    *points    = closure;
2404   ierr = DMRestoreWorkArray(dm, maxSize, MPIU_INT, &fifo);CHKERRQ(ierr);
2405   PetscFunctionReturn(0);
2406 }
2407 
2408 /*@C
2409   DMPlexGetTransitiveClosure_Internal - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG with a specified initial orientation
2410 
2411   Not collective
2412 
2413   Input Parameters:
2414 + mesh - The DMPlex
2415 . p - The point, which must lie in the chart set with DMPlexSetChart()
2416 . orientation - The orientation of the point
2417 . useCone - PETSC_TRUE for in-edges,  otherwise use out-edges
2418 - points - If points is NULL on input, internal storage will be returned, otherwise the provided array is used
2419 
2420   Output Parameters:
2421 + numPoints - The number of points in the closure, so points[] is of size 2*numPoints
2422 - points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
2423 
2424   Note:
2425   If using internal storage (points is NULL on input), each call overwrites the last output.
2426 
2427   Fortran Notes:
2428   Since it returns an array, this routine is only available in Fortran 90, and you must
2429   include petsc.h90 in your code.
2430 
2431   The numPoints argument is not present in the Fortran 90 binding since it is internal to the array.
2432 
2433   Level: beginner
2434 
2435 .seealso: DMPlexRestoreTransitiveClosure(), DMPlexCreate(), DMPlexSetCone(), DMPlexSetChart(), DMPlexGetCone()
2436 @*/
2437 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
2438 {
2439   DM_Plex        *mesh = (DM_Plex*) dm->data;
2440   PetscInt       *closure, *fifo;
2441   const PetscInt *tmp = NULL, *tmpO = NULL;
2442   PetscInt        tmpSize, t;
2443   PetscInt        depth       = 0, maxSize;
2444   PetscInt        closureSize = 2, fifoSize = 0, fifoStart = 0;
2445   PetscErrorCode  ierr;
2446 
2447   PetscFunctionBegin;
2448   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2449   ierr    = DMPlexGetDepth(dm, &depth);CHKERRQ(ierr);
2450   /* This is only 1-level */
2451   if (useCone) {
2452     ierr = DMPlexGetConeSize(dm, p, &tmpSize);CHKERRQ(ierr);
2453     ierr = DMPlexGetCone(dm, p, &tmp);CHKERRQ(ierr);
2454     ierr = DMPlexGetConeOrientation(dm, p, &tmpO);CHKERRQ(ierr);
2455   } else {
2456     ierr = DMPlexGetSupportSize(dm, p, &tmpSize);CHKERRQ(ierr);
2457     ierr = DMPlexGetSupport(dm, p, &tmp);CHKERRQ(ierr);
2458   }
2459   if (depth == 1) {
2460     if (*points) {
2461       closure = *points;
2462     } else {
2463       maxSize = 2*(PetscMax(mesh->maxConeSize, mesh->maxSupportSize)+1);
2464       ierr = DMGetWorkArray(dm, maxSize, MPIU_INT, &closure);CHKERRQ(ierr);
2465     }
2466     closure[0] = p; closure[1] = ornt;
2467     for (t = 0; t < tmpSize; ++t, closureSize += 2) {
2468       const PetscInt i = ornt >= 0 ? (t+ornt)%tmpSize : (-(ornt+1) + tmpSize-t)%tmpSize;
2469       closure[closureSize]   = tmp[i];
2470       closure[closureSize+1] = tmpO ? tmpO[i] : 0;
2471     }
2472     if (numPoints) *numPoints = closureSize/2;
2473     if (points)    *points    = closure;
2474     PetscFunctionReturn(0);
2475   }
2476   {
2477     PetscInt c, coneSeries, s,supportSeries;
2478 
2479     c = mesh->maxConeSize;
2480     coneSeries = (c > 1) ? ((PetscPowInt(c,depth+1)-1)/(c-1)) : depth+1;
2481     s = mesh->maxSupportSize;
2482     supportSeries = (s > 1) ? ((PetscPowInt(s,depth+1)-1)/(s-1)) : depth+1;
2483     maxSize = 2*PetscMax(coneSeries,supportSeries);
2484   }
2485   ierr    = DMGetWorkArray(dm, maxSize, MPIU_INT, &fifo);CHKERRQ(ierr);
2486   if (*points) {
2487     closure = *points;
2488   } else {
2489     ierr = DMGetWorkArray(dm, maxSize, MPIU_INT, &closure);CHKERRQ(ierr);
2490   }
2491   closure[0] = p; closure[1] = ornt;
2492   for (t = 0; t < tmpSize; ++t, closureSize += 2, fifoSize += 2) {
2493     const PetscInt i  = ornt >= 0 ? (t+ornt)%tmpSize : (-(ornt+1) + tmpSize-t)%tmpSize;
2494     const PetscInt cp = tmp[i];
2495     PetscInt       co = tmpO ? tmpO[i] : 0;
2496 
2497     if (ornt < 0) {
2498       PetscInt childSize, coff;
2499       ierr = DMPlexGetConeSize(dm, cp, &childSize);CHKERRQ(ierr);
2500       coff = co < 0 ? -(tmpO[i]+1) : tmpO[i];
2501       co   = childSize ? -(((coff+childSize-1)%childSize)+1) : 0;
2502     }
2503     closure[closureSize]   = cp;
2504     closure[closureSize+1] = co;
2505     fifo[fifoSize]         = cp;
2506     fifo[fifoSize+1]       = co;
2507   }
2508   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
2509   while (fifoSize - fifoStart) {
2510     const PetscInt q   = fifo[fifoStart];
2511     const PetscInt o   = fifo[fifoStart+1];
2512     const PetscInt rev = o >= 0 ? 0 : 1;
2513     const PetscInt off = rev ? -(o+1) : o;
2514 
2515     if (useCone) {
2516       ierr = DMPlexGetConeSize(dm, q, &tmpSize);CHKERRQ(ierr);
2517       ierr = DMPlexGetCone(dm, q, &tmp);CHKERRQ(ierr);
2518       ierr = DMPlexGetConeOrientation(dm, q, &tmpO);CHKERRQ(ierr);
2519     } else {
2520       ierr = DMPlexGetSupportSize(dm, q, &tmpSize);CHKERRQ(ierr);
2521       ierr = DMPlexGetSupport(dm, q, &tmp);CHKERRQ(ierr);
2522       tmpO = NULL;
2523     }
2524     for (t = 0; t < tmpSize; ++t) {
2525       const PetscInt i  = ((rev ? tmpSize-t : t) + off)%tmpSize;
2526       const PetscInt cp = tmp[i];
2527       /* Must propogate orientation: When we reverse orientation, we both reverse the direction of iteration and start at the other end of the chain. */
2528       /* HACK: It is worse to get the size here, than to change the interpretation of -(*+1)
2529        const PetscInt co = tmpO ? (rev ? -(tmpO[i]+1) : tmpO[i]) : 0; */
2530       PetscInt       co = tmpO ? tmpO[i] : 0;
2531       PetscInt       c;
2532 
2533       if (rev) {
2534         PetscInt childSize, coff;
2535         ierr = DMPlexGetConeSize(dm, cp, &childSize);CHKERRQ(ierr);
2536         coff = tmpO[i] < 0 ? -(tmpO[i]+1) : tmpO[i];
2537         co   = childSize ? -(((coff+childSize-1)%childSize)+1) : 0;
2538       }
2539       /* Check for duplicate */
2540       for (c = 0; c < closureSize; c += 2) {
2541         if (closure[c] == cp) break;
2542       }
2543       if (c == closureSize) {
2544         closure[closureSize]   = cp;
2545         closure[closureSize+1] = co;
2546         fifo[fifoSize]         = cp;
2547         fifo[fifoSize+1]       = co;
2548         closureSize           += 2;
2549         fifoSize              += 2;
2550       }
2551     }
2552     fifoStart += 2;
2553   }
2554   if (numPoints) *numPoints = closureSize/2;
2555   if (points)    *points    = closure;
2556   ierr = DMRestoreWorkArray(dm, maxSize, MPIU_INT, &fifo);CHKERRQ(ierr);
2557   PetscFunctionReturn(0);
2558 }
2559 
2560 /*@C
2561   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
2562 
2563   Not collective
2564 
2565   Input Parameters:
2566 + mesh - The DMPlex
2567 . p - The point, which must lie in the chart set with DMPlexSetChart()
2568 . useCone - PETSC_TRUE for in-edges,  otherwise use out-edges
2569 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints, zeroed on exit
2570 - points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...], zeroed on exit
2571 
2572   Note:
2573   If not using internal storage (points is not NULL on input), this call is unnecessary
2574 
2575   Fortran Notes:
2576   Since it returns an array, this routine is only available in Fortran 90, and you must
2577   include petsc.h90 in your code.
2578 
2579   The numPoints argument is not present in the Fortran 90 binding since it is internal to the array.
2580 
2581   Level: beginner
2582 
2583 .seealso: DMPlexGetTransitiveClosure(), DMPlexCreate(), DMPlexSetCone(), DMPlexSetChart(), DMPlexGetCone()
2584 @*/
2585 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
2586 {
2587   PetscErrorCode ierr;
2588 
2589   PetscFunctionBegin;
2590   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2591   if (numPoints) PetscValidIntPointer(numPoints,4);
2592   if (points) PetscValidPointer(points,5);
2593   ierr = DMRestoreWorkArray(dm, 0, MPIU_INT, points);CHKERRQ(ierr);
2594   if (numPoints) *numPoints = 0;
2595   PetscFunctionReturn(0);
2596 }
2597 
2598 /*@
2599   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
2600 
2601   Not collective
2602 
2603   Input Parameter:
2604 . mesh - The DMPlex
2605 
2606   Output Parameters:
2607 + maxConeSize - The maximum number of in-edges
2608 - maxSupportSize - The maximum number of out-edges
2609 
2610   Level: beginner
2611 
2612 .seealso: DMPlexCreate(), DMPlexSetConeSize(), DMPlexSetChart()
2613 @*/
2614 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
2615 {
2616   DM_Plex *mesh = (DM_Plex*) dm->data;
2617 
2618   PetscFunctionBegin;
2619   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2620   if (maxConeSize)    *maxConeSize    = mesh->maxConeSize;
2621   if (maxSupportSize) *maxSupportSize = mesh->maxSupportSize;
2622   PetscFunctionReturn(0);
2623 }
2624 
2625 PetscErrorCode DMSetUp_Plex(DM dm)
2626 {
2627   DM_Plex       *mesh = (DM_Plex*) dm->data;
2628   PetscInt       size;
2629   PetscErrorCode ierr;
2630 
2631   PetscFunctionBegin;
2632   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2633   ierr = PetscSectionSetUp(mesh->coneSection);CHKERRQ(ierr);
2634   ierr = PetscSectionGetStorageSize(mesh->coneSection, &size);CHKERRQ(ierr);
2635   ierr = PetscMalloc1(size, &mesh->cones);CHKERRQ(ierr);
2636   ierr = PetscCalloc1(size, &mesh->coneOrientations);CHKERRQ(ierr);
2637   if (mesh->maxSupportSize) {
2638     ierr = PetscSectionSetUp(mesh->supportSection);CHKERRQ(ierr);
2639     ierr = PetscSectionGetStorageSize(mesh->supportSection, &size);CHKERRQ(ierr);
2640     ierr = PetscMalloc1(size, &mesh->supports);CHKERRQ(ierr);
2641   }
2642   PetscFunctionReturn(0);
2643 }
2644 
2645 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
2646 {
2647   PetscErrorCode ierr;
2648 
2649   PetscFunctionBegin;
2650   if (subdm) {ierr = DMClone(dm, subdm);CHKERRQ(ierr);}
2651   ierr = DMCreateSectionSubDM(dm, numFields, fields, is, subdm);CHKERRQ(ierr);
2652   if (subdm) {(*subdm)->useNatural = dm->useNatural;}
2653   if (dm->useNatural && dm->sfMigration) {
2654     PetscSF        sfMigrationInv,sfNatural;
2655     PetscSection   section, sectionSeq;
2656 
2657     (*subdm)->sfMigration = dm->sfMigration;
2658     ierr = PetscObjectReference((PetscObject) dm->sfMigration);CHKERRQ(ierr);
2659     ierr = DMGetLocalSection((*subdm), &section);CHKERRQ(ierr);
2660     ierr = PetscSFCreateInverseSF((*subdm)->sfMigration, &sfMigrationInv);CHKERRQ(ierr);
2661     ierr = PetscSectionCreate(PetscObjectComm((PetscObject) (*subdm)), &sectionSeq);CHKERRQ(ierr);
2662     ierr = PetscSFDistributeSection(sfMigrationInv, section, NULL, sectionSeq);CHKERRQ(ierr);
2663 
2664     ierr = DMPlexCreateGlobalToNaturalSF(*subdm, sectionSeq, (*subdm)->sfMigration, &sfNatural);CHKERRQ(ierr);
2665     (*subdm)->sfNatural = sfNatural;
2666     ierr = PetscSectionDestroy(&sectionSeq);CHKERRQ(ierr);
2667     ierr = PetscSFDestroy(&sfMigrationInv);CHKERRQ(ierr);
2668   }
2669   PetscFunctionReturn(0);
2670 }
2671 
2672 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
2673 {
2674   PetscErrorCode ierr;
2675   PetscInt       i = 0;
2676 
2677   PetscFunctionBegin;
2678   ierr = DMClone(dms[0], superdm);CHKERRQ(ierr);
2679   ierr = DMCreateSectionSuperDM(dms, len, is, superdm);CHKERRQ(ierr);
2680   (*superdm)->useNatural = PETSC_FALSE;
2681   for (i = 0; i < len; i++){
2682     if (dms[i]->useNatural && dms[i]->sfMigration) {
2683       PetscSF        sfMigrationInv,sfNatural;
2684       PetscSection   section, sectionSeq;
2685 
2686       (*superdm)->sfMigration = dms[i]->sfMigration;
2687       ierr = PetscObjectReference((PetscObject) dms[i]->sfMigration);CHKERRQ(ierr);
2688       (*superdm)->useNatural = PETSC_TRUE;
2689       ierr = DMGetLocalSection((*superdm), &section);CHKERRQ(ierr);
2690       ierr = PetscSFCreateInverseSF((*superdm)->sfMigration, &sfMigrationInv);CHKERRQ(ierr);
2691       ierr = PetscSectionCreate(PetscObjectComm((PetscObject) (*superdm)), &sectionSeq);CHKERRQ(ierr);
2692       ierr = PetscSFDistributeSection(sfMigrationInv, section, NULL, sectionSeq);CHKERRQ(ierr);
2693 
2694       ierr = DMPlexCreateGlobalToNaturalSF(*superdm, sectionSeq, (*superdm)->sfMigration, &sfNatural);CHKERRQ(ierr);
2695       (*superdm)->sfNatural = sfNatural;
2696       ierr = PetscSectionDestroy(&sectionSeq);CHKERRQ(ierr);
2697       ierr = PetscSFDestroy(&sfMigrationInv);CHKERRQ(ierr);
2698       break;
2699     }
2700   }
2701   PetscFunctionReturn(0);
2702 }
2703 
2704 /*@
2705   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
2706 
2707   Not collective
2708 
2709   Input Parameter:
2710 . mesh - The DMPlex
2711 
2712   Output Parameter:
2713 
2714   Note:
2715   This should be called after all calls to DMPlexSetCone()
2716 
2717   Level: beginner
2718 
2719 .seealso: DMPlexCreate(), DMPlexSetChart(), DMPlexSetConeSize(), DMPlexSetCone()
2720 @*/
2721 PetscErrorCode DMPlexSymmetrize(DM dm)
2722 {
2723   DM_Plex       *mesh = (DM_Plex*) dm->data;
2724   PetscInt      *offsets;
2725   PetscInt       supportSize;
2726   PetscInt       pStart, pEnd, p;
2727   PetscErrorCode ierr;
2728 
2729   PetscFunctionBegin;
2730   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2731   if (mesh->supports) SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
2732   ierr = PetscLogEventBegin(DMPLEX_Symmetrize,dm,0,0,0);CHKERRQ(ierr);
2733   /* Calculate support sizes */
2734   ierr = DMPlexGetChart(dm, &pStart, &pEnd);CHKERRQ(ierr);
2735   for (p = pStart; p < pEnd; ++p) {
2736     PetscInt dof, off, c;
2737 
2738     ierr = PetscSectionGetDof(mesh->coneSection, p, &dof);CHKERRQ(ierr);
2739     ierr = PetscSectionGetOffset(mesh->coneSection, p, &off);CHKERRQ(ierr);
2740     for (c = off; c < off+dof; ++c) {
2741       ierr = PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1);CHKERRQ(ierr);
2742     }
2743   }
2744   for (p = pStart; p < pEnd; ++p) {
2745     PetscInt dof;
2746 
2747     ierr = PetscSectionGetDof(mesh->supportSection, p, &dof);CHKERRQ(ierr);
2748 
2749     mesh->maxSupportSize = PetscMax(mesh->maxSupportSize, dof);
2750   }
2751   ierr = PetscSectionSetUp(mesh->supportSection);CHKERRQ(ierr);
2752   /* Calculate supports */
2753   ierr = PetscSectionGetStorageSize(mesh->supportSection, &supportSize);CHKERRQ(ierr);
2754   ierr = PetscMalloc1(supportSize, &mesh->supports);CHKERRQ(ierr);
2755   ierr = PetscCalloc1(pEnd - pStart, &offsets);CHKERRQ(ierr);
2756   for (p = pStart; p < pEnd; ++p) {
2757     PetscInt dof, off, c;
2758 
2759     ierr = PetscSectionGetDof(mesh->coneSection, p, &dof);CHKERRQ(ierr);
2760     ierr = PetscSectionGetOffset(mesh->coneSection, p, &off);CHKERRQ(ierr);
2761     for (c = off; c < off+dof; ++c) {
2762       const PetscInt q = mesh->cones[c];
2763       PetscInt       offS;
2764 
2765       ierr = PetscSectionGetOffset(mesh->supportSection, q, &offS);CHKERRQ(ierr);
2766 
2767       mesh->supports[offS+offsets[q]] = p;
2768       ++offsets[q];
2769     }
2770   }
2771   ierr = PetscFree(offsets);CHKERRQ(ierr);
2772   ierr = PetscLogEventEnd(DMPLEX_Symmetrize,dm,0,0,0);CHKERRQ(ierr);
2773   PetscFunctionReturn(0);
2774 }
2775 
2776 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
2777 {
2778   IS             stratumIS;
2779   PetscErrorCode ierr;
2780 
2781   PetscFunctionBegin;
2782   if (pStart >= pEnd) PetscFunctionReturn(0);
2783   if (PetscDefined(USE_DEBUG)) {
2784     PetscInt  qStart, qEnd, numLevels, level;
2785     PetscBool overlap = PETSC_FALSE;
2786     ierr = DMLabelGetNumValues(label, &numLevels);CHKERRQ(ierr);
2787     for (level = 0; level < numLevels; level++) {
2788       ierr = DMLabelGetStratumBounds(label, level, &qStart, &qEnd);CHKERRQ(ierr);
2789       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {overlap = PETSC_TRUE; break;}
2790     }
2791     if (overlap) SETERRQ6(PETSC_COMM_SELF, PETSC_ERR_PLIB, "New depth %D range [%D,%D) overlaps with depth %D range [%D,%D)", depth, pStart, pEnd, level, qStart, qEnd);
2792   }
2793   ierr = ISCreateStride(PETSC_COMM_SELF, pEnd-pStart, pStart, 1, &stratumIS);CHKERRQ(ierr);
2794   ierr = DMLabelSetStratumIS(label, depth, stratumIS);CHKERRQ(ierr);
2795   ierr = ISDestroy(&stratumIS);CHKERRQ(ierr);
2796   PetscFunctionReturn(0);
2797 }
2798 
2799 /*@
2800   DMPlexStratify - The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
2801   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram). The strata group all points of the
2802   same grade, and this function calculates the strata. This grade can be seen as the height (or depth) of the point in
2803   the DAG.
2804 
2805   Collective on dm
2806 
2807   Input Parameter:
2808 . mesh - The DMPlex
2809 
2810   Output Parameter:
2811 
2812   Notes:
2813   Concretely, DMPlexStratify() creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
2814   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
2815   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through DMPlexGetDepthLabel() or DMPlexGetDepthStratum(), or
2816   manually via DMGetLabel().  The height is defined implicitly by height = maxDimension - depth, and can be accessed
2817   via DMPlexGetHeightStratum().  For example, cells have height 0 and faces have height 1.
2818 
2819   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
2820   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
2821   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
2822   to interpolate only that one (e0), so that
2823 $  cone(c0) = {e0, v2}
2824 $  cone(e0) = {v0, v1}
2825   If DMPlexStratify() is run on this mesh, it will give depths
2826 $  depth 0 = {v0, v1, v2}
2827 $  depth 1 = {e0, c0}
2828   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
2829 
2830   DMPlexStratify() should be called after all calls to DMPlexSymmetrize()
2831 
2832   Level: beginner
2833 
2834 .seealso: DMPlexCreate(), DMPlexSymmetrize(), DMPlexComputeCellTypes()
2835 @*/
2836 PetscErrorCode DMPlexStratify(DM dm)
2837 {
2838   DM_Plex       *mesh = (DM_Plex*) dm->data;
2839   DMLabel        label;
2840   PetscInt       pStart, pEnd, p;
2841   PetscInt       numRoots = 0, numLeaves = 0;
2842   PetscErrorCode ierr;
2843 
2844   PetscFunctionBegin;
2845   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2846   ierr = PetscLogEventBegin(DMPLEX_Stratify,dm,0,0,0);CHKERRQ(ierr);
2847 
2848   /* Create depth label */
2849   ierr = DMPlexGetChart(dm, &pStart, &pEnd);CHKERRQ(ierr);
2850   ierr = DMCreateLabel(dm, "depth");CHKERRQ(ierr);
2851   ierr = DMPlexGetDepthLabel(dm, &label);CHKERRQ(ierr);
2852 
2853   {
2854     /* Initialize roots and count leaves */
2855     PetscInt sMin = PETSC_MAX_INT;
2856     PetscInt sMax = PETSC_MIN_INT;
2857     PetscInt coneSize, supportSize;
2858 
2859     for (p = pStart; p < pEnd; ++p) {
2860       ierr = DMPlexGetConeSize(dm, p, &coneSize);CHKERRQ(ierr);
2861       ierr = DMPlexGetSupportSize(dm, p, &supportSize);CHKERRQ(ierr);
2862       if (!coneSize && supportSize) {
2863         sMin = PetscMin(p, sMin);
2864         sMax = PetscMax(p, sMax);
2865         ++numRoots;
2866       } else if (!supportSize && coneSize) {
2867         ++numLeaves;
2868       } else if (!supportSize && !coneSize) {
2869         /* Isolated points */
2870         sMin = PetscMin(p, sMin);
2871         sMax = PetscMax(p, sMax);
2872       }
2873     }
2874     ierr = DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax+1);CHKERRQ(ierr);
2875   }
2876 
2877   if (numRoots + numLeaves == (pEnd - pStart)) {
2878     PetscInt sMin = PETSC_MAX_INT;
2879     PetscInt sMax = PETSC_MIN_INT;
2880     PetscInt coneSize, supportSize;
2881 
2882     for (p = pStart; p < pEnd; ++p) {
2883       ierr = DMPlexGetConeSize(dm, p, &coneSize);CHKERRQ(ierr);
2884       ierr = DMPlexGetSupportSize(dm, p, &supportSize);CHKERRQ(ierr);
2885       if (!supportSize && coneSize) {
2886         sMin = PetscMin(p, sMin);
2887         sMax = PetscMax(p, sMax);
2888       }
2889     }
2890     ierr = DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax+1);CHKERRQ(ierr);
2891   } else {
2892     PetscInt level = 0;
2893     PetscInt qStart, qEnd, q;
2894 
2895     ierr = DMLabelGetStratumBounds(label, level, &qStart, &qEnd);CHKERRQ(ierr);
2896     while (qEnd > qStart) {
2897       PetscInt sMin = PETSC_MAX_INT;
2898       PetscInt sMax = PETSC_MIN_INT;
2899 
2900       for (q = qStart; q < qEnd; ++q) {
2901         const PetscInt *support;
2902         PetscInt        supportSize, s;
2903 
2904         ierr = DMPlexGetSupportSize(dm, q, &supportSize);CHKERRQ(ierr);
2905         ierr = DMPlexGetSupport(dm, q, &support);CHKERRQ(ierr);
2906         for (s = 0; s < supportSize; ++s) {
2907           sMin = PetscMin(support[s], sMin);
2908           sMax = PetscMax(support[s], sMax);
2909         }
2910       }
2911       ierr = DMLabelGetNumValues(label, &level);CHKERRQ(ierr);
2912       ierr = DMPlexCreateDepthStratum(dm, label, level, sMin, sMax+1);CHKERRQ(ierr);
2913       ierr = DMLabelGetStratumBounds(label, level, &qStart, &qEnd);CHKERRQ(ierr);
2914     }
2915   }
2916   { /* just in case there is an empty process */
2917     PetscInt numValues, maxValues = 0, v;
2918 
2919     ierr = DMLabelGetNumValues(label, &numValues);CHKERRQ(ierr);
2920     ierr = MPI_Allreduce(&numValues,&maxValues,1,MPIU_INT,MPI_MAX,PetscObjectComm((PetscObject)dm));CHKERRQ(ierr);
2921     for (v = numValues; v < maxValues; v++) {
2922       ierr = DMLabelAddStratum(label, v);CHKERRQ(ierr);
2923     }
2924   }
2925   ierr = PetscObjectStateGet((PetscObject) label, &mesh->depthState);CHKERRQ(ierr);
2926   ierr = PetscLogEventEnd(DMPLEX_Stratify,dm,0,0,0);CHKERRQ(ierr);
2927   PetscFunctionReturn(0);
2928 }
2929 
2930 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
2931 {
2932   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
2933   PetscInt       dim, depth, pheight, coneSize;
2934   PetscErrorCode ierr;
2935 
2936   PetscFunctionBeginHot;
2937   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
2938   ierr = DMPlexGetDepth(dm, &depth);CHKERRQ(ierr);
2939   ierr = DMPlexGetConeSize(dm, p, &coneSize);CHKERRQ(ierr);
2940   pheight = depth - pdepth;
2941   if (depth <= 1) {
2942     switch (pdepth) {
2943       case 0: ct = DM_POLYTOPE_POINT;break;
2944       case 1:
2945         switch (coneSize) {
2946           case 2: ct = DM_POLYTOPE_SEGMENT;break;
2947           case 3: ct = DM_POLYTOPE_TRIANGLE;break;
2948           case 4:
2949           switch (dim) {
2950             case 2: ct = DM_POLYTOPE_QUADRILATERAL;break;
2951             case 3: ct = DM_POLYTOPE_TETRAHEDRON;break;
2952             default: break;
2953           }
2954           break;
2955         case 6: ct = DM_POLYTOPE_TRI_PRISM_TENSOR;break;
2956         case 8: ct = DM_POLYTOPE_HEXAHEDRON;break;
2957         default: break;
2958       }
2959     }
2960   } else {
2961     if (pdepth == 0) {
2962       ct = DM_POLYTOPE_POINT;
2963     } else if (pheight == 0) {
2964       switch (dim) {
2965         case 1:
2966           switch (coneSize) {
2967             case 2: ct = DM_POLYTOPE_SEGMENT;break;
2968             default: break;
2969           }
2970           break;
2971         case 2:
2972           switch (coneSize) {
2973             case 3: ct = DM_POLYTOPE_TRIANGLE;break;
2974             case 4: ct = DM_POLYTOPE_QUADRILATERAL;break;
2975             default: break;
2976           }
2977           break;
2978         case 3:
2979           switch (coneSize) {
2980             case 4: ct = DM_POLYTOPE_TETRAHEDRON;break;
2981             case 5: ct = DM_POLYTOPE_TRI_PRISM_TENSOR;break;
2982             case 6: ct = DM_POLYTOPE_HEXAHEDRON;break;
2983             default: break;
2984           }
2985           break;
2986         default: break;
2987       }
2988     } else if (pheight > 0) {
2989       switch (coneSize) {
2990         case 2: ct = DM_POLYTOPE_SEGMENT;break;
2991         case 3: ct = DM_POLYTOPE_TRIANGLE;break;
2992         case 4: ct = DM_POLYTOPE_QUADRILATERAL;break;
2993         default: break;
2994       }
2995     }
2996   }
2997   *pt = ct;
2998   PetscFunctionReturn(0);
2999 }
3000 
3001 /*@
3002   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
3003 
3004   Collective on dm
3005 
3006   Input Parameter:
3007 . mesh - The DMPlex
3008 
3009   DMPlexComputeCellTypes() should be called after all calls to DMPlexSymmetrize() and DMPlexStratify()
3010 
3011   Level: developer
3012 
3013   Note: This function is normally called automatically by Plex when a cell type is requested. It creates an
3014   internal DMLabel named "celltype" which can be directly accessed using DMGetLabel(). A user may disable
3015   automatic creation by creating the label manually, using DMCreateLabel(dm, "celltype").
3016 
3017 .seealso: DMPlexCreate(), DMPlexSymmetrize(), DMPlexStratify(), DMGetLabel(), DMCreateLabel()
3018 @*/
3019 PetscErrorCode DMPlexComputeCellTypes(DM dm)
3020 {
3021   DM_Plex       *mesh;
3022   DMLabel        ctLabel;
3023   PetscInt       pStart, pEnd, p;
3024   PetscErrorCode ierr;
3025 
3026   PetscFunctionBegin;
3027   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3028   mesh = (DM_Plex *) dm->data;
3029   ierr = DMCreateLabel(dm, "celltype");CHKERRQ(ierr);
3030   ierr = DMPlexGetCellTypeLabel(dm, &ctLabel);CHKERRQ(ierr);
3031   ierr = DMPlexGetChart(dm, &pStart, &pEnd);CHKERRQ(ierr);
3032   for (p = pStart; p < pEnd; ++p) {
3033     DMPolytopeType ct;
3034     PetscInt       pdepth;
3035 
3036     ierr = DMPlexGetPointDepth(dm, p, &pdepth);CHKERRQ(ierr);
3037     ierr = DMPlexComputeCellType_Internal(dm, p, pdepth, &ct);CHKERRQ(ierr);
3038     if (ct == DM_POLYTOPE_UNKNOWN) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %D is screwed up", p);
3039     ierr = DMLabelSetValue(ctLabel, p, ct);CHKERRQ(ierr);
3040   }
3041   ierr = PetscObjectStateGet((PetscObject) ctLabel, &mesh->celltypeState);CHKERRQ(ierr);
3042   ierr = PetscObjectViewFromOptions((PetscObject) ctLabel, NULL, "-dm_plex_celltypes_view");CHKERRQ(ierr);
3043   PetscFunctionReturn(0);
3044 }
3045 
3046 /*@C
3047   DMPlexGetJoin - Get an array for the join of the set of points
3048 
3049   Not Collective
3050 
3051   Input Parameters:
3052 + dm - The DMPlex object
3053 . numPoints - The number of input points for the join
3054 - points - The input points
3055 
3056   Output Parameters:
3057 + numCoveredPoints - The number of points in the join
3058 - coveredPoints - The points in the join
3059 
3060   Level: intermediate
3061 
3062   Note: Currently, this is restricted to a single level join
3063 
3064   Fortran Notes:
3065   Since it returns an array, this routine is only available in Fortran 90, and you must
3066   include petsc.h90 in your code.
3067 
3068   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
3069 
3070 .seealso: DMPlexRestoreJoin(), DMPlexGetMeet()
3071 @*/
3072 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
3073 {
3074   DM_Plex       *mesh = (DM_Plex*) dm->data;
3075   PetscInt      *join[2];
3076   PetscInt       joinSize, i = 0;
3077   PetscInt       dof, off, p, c, m;
3078   PetscErrorCode ierr;
3079 
3080   PetscFunctionBegin;
3081   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3082   PetscValidIntPointer(points, 3);
3083   PetscValidIntPointer(numCoveredPoints, 4);
3084   PetscValidPointer(coveredPoints, 5);
3085   ierr = DMGetWorkArray(dm, mesh->maxSupportSize, MPIU_INT, &join[0]);CHKERRQ(ierr);
3086   ierr = DMGetWorkArray(dm, mesh->maxSupportSize, MPIU_INT, &join[1]);CHKERRQ(ierr);
3087   /* Copy in support of first point */
3088   ierr = PetscSectionGetDof(mesh->supportSection, points[0], &dof);CHKERRQ(ierr);
3089   ierr = PetscSectionGetOffset(mesh->supportSection, points[0], &off);CHKERRQ(ierr);
3090   for (joinSize = 0; joinSize < dof; ++joinSize) {
3091     join[i][joinSize] = mesh->supports[off+joinSize];
3092   }
3093   /* Check each successive support */
3094   for (p = 1; p < numPoints; ++p) {
3095     PetscInt newJoinSize = 0;
3096 
3097     ierr = PetscSectionGetDof(mesh->supportSection, points[p], &dof);CHKERRQ(ierr);
3098     ierr = PetscSectionGetOffset(mesh->supportSection, points[p], &off);CHKERRQ(ierr);
3099     for (c = 0; c < dof; ++c) {
3100       const PetscInt point = mesh->supports[off+c];
3101 
3102       for (m = 0; m < joinSize; ++m) {
3103         if (point == join[i][m]) {
3104           join[1-i][newJoinSize++] = point;
3105           break;
3106         }
3107       }
3108     }
3109     joinSize = newJoinSize;
3110     i        = 1-i;
3111   }
3112   *numCoveredPoints = joinSize;
3113   *coveredPoints    = join[i];
3114   ierr              = DMRestoreWorkArray(dm, mesh->maxSupportSize, MPIU_INT, &join[1-i]);CHKERRQ(ierr);
3115   PetscFunctionReturn(0);
3116 }
3117 
3118 /*@C
3119   DMPlexRestoreJoin - Restore an array for the join of the set of points
3120 
3121   Not Collective
3122 
3123   Input Parameters:
3124 + dm - The DMPlex object
3125 . numPoints - The number of input points for the join
3126 - points - The input points
3127 
3128   Output Parameters:
3129 + numCoveredPoints - The number of points in the join
3130 - coveredPoints - The points in the join
3131 
3132   Fortran Notes:
3133   Since it returns an array, this routine is only available in Fortran 90, and you must
3134   include petsc.h90 in your code.
3135 
3136   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
3137 
3138   Level: intermediate
3139 
3140 .seealso: DMPlexGetJoin(), DMPlexGetFullJoin(), DMPlexGetMeet()
3141 @*/
3142 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
3143 {
3144   PetscErrorCode ierr;
3145 
3146   PetscFunctionBegin;
3147   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3148   if (points) PetscValidIntPointer(points,3);
3149   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints,4);
3150   PetscValidPointer(coveredPoints, 5);
3151   ierr = DMRestoreWorkArray(dm, 0, MPIU_INT, (void*) coveredPoints);CHKERRQ(ierr);
3152   if (numCoveredPoints) *numCoveredPoints = 0;
3153   PetscFunctionReturn(0);
3154 }
3155 
3156 /*@C
3157   DMPlexGetFullJoin - Get an array for the join of the set of points
3158 
3159   Not Collective
3160 
3161   Input Parameters:
3162 + dm - The DMPlex object
3163 . numPoints - The number of input points for the join
3164 - points - The input points
3165 
3166   Output Parameters:
3167 + numCoveredPoints - The number of points in the join
3168 - coveredPoints - The points in the join
3169 
3170   Fortran Notes:
3171   Since it returns an array, this routine is only available in Fortran 90, and you must
3172   include petsc.h90 in your code.
3173 
3174   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
3175 
3176   Level: intermediate
3177 
3178 .seealso: DMPlexGetJoin(), DMPlexRestoreJoin(), DMPlexGetMeet()
3179 @*/
3180 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
3181 {
3182   DM_Plex       *mesh = (DM_Plex*) dm->data;
3183   PetscInt      *offsets, **closures;
3184   PetscInt      *join[2];
3185   PetscInt       depth = 0, maxSize, joinSize = 0, i = 0;
3186   PetscInt       p, d, c, m, ms;
3187   PetscErrorCode ierr;
3188 
3189   PetscFunctionBegin;
3190   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3191   PetscValidIntPointer(points, 3);
3192   PetscValidIntPointer(numCoveredPoints, 4);
3193   PetscValidPointer(coveredPoints, 5);
3194 
3195   ierr    = DMPlexGetDepth(dm, &depth);CHKERRQ(ierr);
3196   ierr    = PetscCalloc1(numPoints, &closures);CHKERRQ(ierr);
3197   ierr    = DMGetWorkArray(dm, numPoints*(depth+2), MPIU_INT, &offsets);CHKERRQ(ierr);
3198   ms      = mesh->maxSupportSize;
3199   maxSize = (ms > 1) ? ((PetscPowInt(ms,depth+1)-1)/(ms-1)) : depth + 1;
3200   ierr    = DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]);CHKERRQ(ierr);
3201   ierr    = DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]);CHKERRQ(ierr);
3202 
3203   for (p = 0; p < numPoints; ++p) {
3204     PetscInt closureSize;
3205 
3206     ierr = DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]);CHKERRQ(ierr);
3207 
3208     offsets[p*(depth+2)+0] = 0;
3209     for (d = 0; d < depth+1; ++d) {
3210       PetscInt pStart, pEnd, i;
3211 
3212       ierr = DMPlexGetDepthStratum(dm, d, &pStart, &pEnd);CHKERRQ(ierr);
3213       for (i = offsets[p*(depth+2)+d]; i < closureSize; ++i) {
3214         if ((pStart > closures[p][i*2]) || (pEnd <= closures[p][i*2])) {
3215           offsets[p*(depth+2)+d+1] = i;
3216           break;
3217         }
3218       }
3219       if (i == closureSize) offsets[p*(depth+2)+d+1] = i;
3220     }
3221     if (offsets[p*(depth+2)+depth+1] != closureSize) SETERRQ2(PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Total size of closure %D should be %D", offsets[p*(depth+2)+depth+1], closureSize);
3222   }
3223   for (d = 0; d < depth+1; ++d) {
3224     PetscInt dof;
3225 
3226     /* Copy in support of first point */
3227     dof = offsets[d+1] - offsets[d];
3228     for (joinSize = 0; joinSize < dof; ++joinSize) {
3229       join[i][joinSize] = closures[0][(offsets[d]+joinSize)*2];
3230     }
3231     /* Check each successive cone */
3232     for (p = 1; p < numPoints && joinSize; ++p) {
3233       PetscInt newJoinSize = 0;
3234 
3235       dof = offsets[p*(depth+2)+d+1] - offsets[p*(depth+2)+d];
3236       for (c = 0; c < dof; ++c) {
3237         const PetscInt point = closures[p][(offsets[p*(depth+2)+d]+c)*2];
3238 
3239         for (m = 0; m < joinSize; ++m) {
3240           if (point == join[i][m]) {
3241             join[1-i][newJoinSize++] = point;
3242             break;
3243           }
3244         }
3245       }
3246       joinSize = newJoinSize;
3247       i        = 1-i;
3248     }
3249     if (joinSize) break;
3250   }
3251   *numCoveredPoints = joinSize;
3252   *coveredPoints    = join[i];
3253   for (p = 0; p < numPoints; ++p) {
3254     ierr = DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]);CHKERRQ(ierr);
3255   }
3256   ierr = PetscFree(closures);CHKERRQ(ierr);
3257   ierr = DMRestoreWorkArray(dm, numPoints*(depth+2), MPIU_INT, &offsets);CHKERRQ(ierr);
3258   ierr = DMRestoreWorkArray(dm, mesh->maxSupportSize, MPIU_INT, &join[1-i]);CHKERRQ(ierr);
3259   PetscFunctionReturn(0);
3260 }
3261 
3262 /*@C
3263   DMPlexGetMeet - Get an array for the meet of the set of points
3264 
3265   Not Collective
3266 
3267   Input Parameters:
3268 + dm - The DMPlex object
3269 . numPoints - The number of input points for the meet
3270 - points - The input points
3271 
3272   Output Parameters:
3273 + numCoveredPoints - The number of points in the meet
3274 - coveredPoints - The points in the meet
3275 
3276   Level: intermediate
3277 
3278   Note: Currently, this is restricted to a single level meet
3279 
3280   Fortran Notes:
3281   Since it returns an array, this routine is only available in Fortran 90, and you must
3282   include petsc.h90 in your code.
3283 
3284   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
3285 
3286 .seealso: DMPlexRestoreMeet(), DMPlexGetJoin()
3287 @*/
3288 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
3289 {
3290   DM_Plex       *mesh = (DM_Plex*) dm->data;
3291   PetscInt      *meet[2];
3292   PetscInt       meetSize, i = 0;
3293   PetscInt       dof, off, p, c, m;
3294   PetscErrorCode ierr;
3295 
3296   PetscFunctionBegin;
3297   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3298   PetscValidPointer(points, 2);
3299   PetscValidPointer(numCoveringPoints, 3);
3300   PetscValidPointer(coveringPoints, 4);
3301   ierr = DMGetWorkArray(dm, mesh->maxConeSize, MPIU_INT, &meet[0]);CHKERRQ(ierr);
3302   ierr = DMGetWorkArray(dm, mesh->maxConeSize, MPIU_INT, &meet[1]);CHKERRQ(ierr);
3303   /* Copy in cone of first point */
3304   ierr = PetscSectionGetDof(mesh->coneSection, points[0], &dof);CHKERRQ(ierr);
3305   ierr = PetscSectionGetOffset(mesh->coneSection, points[0], &off);CHKERRQ(ierr);
3306   for (meetSize = 0; meetSize < dof; ++meetSize) {
3307     meet[i][meetSize] = mesh->cones[off+meetSize];
3308   }
3309   /* Check each successive cone */
3310   for (p = 1; p < numPoints; ++p) {
3311     PetscInt newMeetSize = 0;
3312 
3313     ierr = PetscSectionGetDof(mesh->coneSection, points[p], &dof);CHKERRQ(ierr);
3314     ierr = PetscSectionGetOffset(mesh->coneSection, points[p], &off);CHKERRQ(ierr);
3315     for (c = 0; c < dof; ++c) {
3316       const PetscInt point = mesh->cones[off+c];
3317 
3318       for (m = 0; m < meetSize; ++m) {
3319         if (point == meet[i][m]) {
3320           meet[1-i][newMeetSize++] = point;
3321           break;
3322         }
3323       }
3324     }
3325     meetSize = newMeetSize;
3326     i        = 1-i;
3327   }
3328   *numCoveringPoints = meetSize;
3329   *coveringPoints    = meet[i];
3330   ierr               = DMRestoreWorkArray(dm, mesh->maxConeSize, MPIU_INT, &meet[1-i]);CHKERRQ(ierr);
3331   PetscFunctionReturn(0);
3332 }
3333 
3334 /*@C
3335   DMPlexRestoreMeet - Restore an array for the meet of the set of points
3336 
3337   Not Collective
3338 
3339   Input Parameters:
3340 + dm - The DMPlex object
3341 . numPoints - The number of input points for the meet
3342 - points - The input points
3343 
3344   Output Parameters:
3345 + numCoveredPoints - The number of points in the meet
3346 - coveredPoints - The points in the meet
3347 
3348   Level: intermediate
3349 
3350   Fortran Notes:
3351   Since it returns an array, this routine is only available in Fortran 90, and you must
3352   include petsc.h90 in your code.
3353 
3354   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
3355 
3356 .seealso: DMPlexGetMeet(), DMPlexGetFullMeet(), DMPlexGetJoin()
3357 @*/
3358 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
3359 {
3360   PetscErrorCode ierr;
3361 
3362   PetscFunctionBegin;
3363   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3364   if (points) PetscValidIntPointer(points,3);
3365   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints,4);
3366   PetscValidPointer(coveredPoints,5);
3367   ierr = DMRestoreWorkArray(dm, 0, MPIU_INT, (void*) coveredPoints);CHKERRQ(ierr);
3368   if (numCoveredPoints) *numCoveredPoints = 0;
3369   PetscFunctionReturn(0);
3370 }
3371 
3372 /*@C
3373   DMPlexGetFullMeet - Get an array for the meet of the set of points
3374 
3375   Not Collective
3376 
3377   Input Parameters:
3378 + dm - The DMPlex object
3379 . numPoints - The number of input points for the meet
3380 - points - The input points
3381 
3382   Output Parameters:
3383 + numCoveredPoints - The number of points in the meet
3384 - coveredPoints - The points in the meet
3385 
3386   Level: intermediate
3387 
3388   Fortran Notes:
3389   Since it returns an array, this routine is only available in Fortran 90, and you must
3390   include petsc.h90 in your code.
3391 
3392   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
3393 
3394 .seealso: DMPlexGetMeet(), DMPlexRestoreMeet(), DMPlexGetJoin()
3395 @*/
3396 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
3397 {
3398   DM_Plex       *mesh = (DM_Plex*) dm->data;
3399   PetscInt      *offsets, **closures;
3400   PetscInt      *meet[2];
3401   PetscInt       height = 0, maxSize, meetSize = 0, i = 0;
3402   PetscInt       p, h, c, m, mc;
3403   PetscErrorCode ierr;
3404 
3405   PetscFunctionBegin;
3406   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3407   PetscValidPointer(points, 2);
3408   PetscValidPointer(numCoveredPoints, 3);
3409   PetscValidPointer(coveredPoints, 4);
3410 
3411   ierr    = DMPlexGetDepth(dm, &height);CHKERRQ(ierr);
3412   ierr    = PetscMalloc1(numPoints, &closures);CHKERRQ(ierr);
3413   ierr    = DMGetWorkArray(dm, numPoints*(height+2), MPIU_INT, &offsets);CHKERRQ(ierr);
3414   mc      = mesh->maxConeSize;
3415   maxSize = (mc > 1) ? ((PetscPowInt(mc,height+1)-1)/(mc-1)) : height + 1;
3416   ierr    = DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]);CHKERRQ(ierr);
3417   ierr    = DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]);CHKERRQ(ierr);
3418 
3419   for (p = 0; p < numPoints; ++p) {
3420     PetscInt closureSize;
3421 
3422     ierr = DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]);CHKERRQ(ierr);
3423 
3424     offsets[p*(height+2)+0] = 0;
3425     for (h = 0; h < height+1; ++h) {
3426       PetscInt pStart, pEnd, i;
3427 
3428       ierr = DMPlexGetHeightStratum(dm, h, &pStart, &pEnd);CHKERRQ(ierr);
3429       for (i = offsets[p*(height+2)+h]; i < closureSize; ++i) {
3430         if ((pStart > closures[p][i*2]) || (pEnd <= closures[p][i*2])) {
3431           offsets[p*(height+2)+h+1] = i;
3432           break;
3433         }
3434       }
3435       if (i == closureSize) offsets[p*(height+2)+h+1] = i;
3436     }
3437     if (offsets[p*(height+2)+height+1] != closureSize) SETERRQ2(PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Total size of closure %D should be %D", offsets[p*(height+2)+height+1], closureSize);
3438   }
3439   for (h = 0; h < height+1; ++h) {
3440     PetscInt dof;
3441 
3442     /* Copy in cone of first point */
3443     dof = offsets[h+1] - offsets[h];
3444     for (meetSize = 0; meetSize < dof; ++meetSize) {
3445       meet[i][meetSize] = closures[0][(offsets[h]+meetSize)*2];
3446     }
3447     /* Check each successive cone */
3448     for (p = 1; p < numPoints && meetSize; ++p) {
3449       PetscInt newMeetSize = 0;
3450 
3451       dof = offsets[p*(height+2)+h+1] - offsets[p*(height+2)+h];
3452       for (c = 0; c < dof; ++c) {
3453         const PetscInt point = closures[p][(offsets[p*(height+2)+h]+c)*2];
3454 
3455         for (m = 0; m < meetSize; ++m) {
3456           if (point == meet[i][m]) {
3457             meet[1-i][newMeetSize++] = point;
3458             break;
3459           }
3460         }
3461       }
3462       meetSize = newMeetSize;
3463       i        = 1-i;
3464     }
3465     if (meetSize) break;
3466   }
3467   *numCoveredPoints = meetSize;
3468   *coveredPoints    = meet[i];
3469   for (p = 0; p < numPoints; ++p) {
3470     ierr = DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]);CHKERRQ(ierr);
3471   }
3472   ierr = PetscFree(closures);CHKERRQ(ierr);
3473   ierr = DMRestoreWorkArray(dm, numPoints*(height+2), MPIU_INT, &offsets);CHKERRQ(ierr);
3474   ierr = DMRestoreWorkArray(dm, mesh->maxConeSize, MPIU_INT, &meet[1-i]);CHKERRQ(ierr);
3475   PetscFunctionReturn(0);
3476 }
3477 
3478 /*@C
3479   DMPlexEqual - Determine if two DMs have the same topology
3480 
3481   Not Collective
3482 
3483   Input Parameters:
3484 + dmA - A DMPlex object
3485 - dmB - A DMPlex object
3486 
3487   Output Parameters:
3488 . equal - PETSC_TRUE if the topologies are identical
3489 
3490   Level: intermediate
3491 
3492   Notes:
3493   We are not solving graph isomorphism, so we do not permutation.
3494 
3495 .seealso: DMPlexGetCone()
3496 @*/
3497 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
3498 {
3499   PetscInt       depth, depthB, pStart, pEnd, pStartB, pEndB, p;
3500   PetscErrorCode ierr;
3501 
3502   PetscFunctionBegin;
3503   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
3504   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
3505   PetscValidPointer(equal, 3);
3506 
3507   *equal = PETSC_FALSE;
3508   ierr = DMPlexGetDepth(dmA, &depth);CHKERRQ(ierr);
3509   ierr = DMPlexGetDepth(dmB, &depthB);CHKERRQ(ierr);
3510   if (depth != depthB) PetscFunctionReturn(0);
3511   ierr = DMPlexGetChart(dmA, &pStart,  &pEnd);CHKERRQ(ierr);
3512   ierr = DMPlexGetChart(dmB, &pStartB, &pEndB);CHKERRQ(ierr);
3513   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(0);
3514   for (p = pStart; p < pEnd; ++p) {
3515     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
3516     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
3517 
3518     ierr = DMPlexGetConeSize(dmA, p, &coneSize);CHKERRQ(ierr);
3519     ierr = DMPlexGetCone(dmA, p, &cone);CHKERRQ(ierr);
3520     ierr = DMPlexGetConeOrientation(dmA, p, &ornt);CHKERRQ(ierr);
3521     ierr = DMPlexGetConeSize(dmB, p, &coneSizeB);CHKERRQ(ierr);
3522     ierr = DMPlexGetCone(dmB, p, &coneB);CHKERRQ(ierr);
3523     ierr = DMPlexGetConeOrientation(dmB, p, &orntB);CHKERRQ(ierr);
3524     if (coneSize != coneSizeB) PetscFunctionReturn(0);
3525     for (c = 0; c < coneSize; ++c) {
3526       if (cone[c] != coneB[c]) PetscFunctionReturn(0);
3527       if (ornt[c] != orntB[c]) PetscFunctionReturn(0);
3528     }
3529     ierr = DMPlexGetSupportSize(dmA, p, &supportSize);CHKERRQ(ierr);
3530     ierr = DMPlexGetSupport(dmA, p, &support);CHKERRQ(ierr);
3531     ierr = DMPlexGetSupportSize(dmB, p, &supportSizeB);CHKERRQ(ierr);
3532     ierr = DMPlexGetSupport(dmB, p, &supportB);CHKERRQ(ierr);
3533     if (supportSize != supportSizeB) PetscFunctionReturn(0);
3534     for (s = 0; s < supportSize; ++s) {
3535       if (support[s] != supportB[s]) PetscFunctionReturn(0);
3536     }
3537   }
3538   *equal = PETSC_TRUE;
3539   PetscFunctionReturn(0);
3540 }
3541 
3542 /*@C
3543   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
3544 
3545   Not Collective
3546 
3547   Input Parameters:
3548 + dm         - The DMPlex
3549 . cellDim    - The cell dimension
3550 - numCorners - The number of vertices on a cell
3551 
3552   Output Parameters:
3553 . numFaceVertices - The number of vertices on a face
3554 
3555   Level: developer
3556 
3557   Notes:
3558   Of course this can only work for a restricted set of symmetric shapes
3559 
3560 .seealso: DMPlexGetCone()
3561 @*/
3562 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
3563 {
3564   MPI_Comm       comm;
3565   PetscErrorCode ierr;
3566 
3567   PetscFunctionBegin;
3568   ierr = PetscObjectGetComm((PetscObject)dm,&comm);CHKERRQ(ierr);
3569   PetscValidPointer(numFaceVertices,3);
3570   switch (cellDim) {
3571   case 0:
3572     *numFaceVertices = 0;
3573     break;
3574   case 1:
3575     *numFaceVertices = 1;
3576     break;
3577   case 2:
3578     switch (numCorners) {
3579     case 3: /* triangle */
3580       *numFaceVertices = 2; /* Edge has 2 vertices */
3581       break;
3582     case 4: /* quadrilateral */
3583       *numFaceVertices = 2; /* Edge has 2 vertices */
3584       break;
3585     case 6: /* quadratic triangle, tri and quad cohesive Lagrange cells */
3586       *numFaceVertices = 3; /* Edge has 3 vertices */
3587       break;
3588     case 9: /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
3589       *numFaceVertices = 3; /* Edge has 3 vertices */
3590       break;
3591     default:
3592       SETERRQ2(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %D for dimension %D", numCorners, cellDim);
3593     }
3594     break;
3595   case 3:
3596     switch (numCorners) {
3597     case 4: /* tetradehdron */
3598       *numFaceVertices = 3; /* Face has 3 vertices */
3599       break;
3600     case 6: /* tet cohesive cells */
3601       *numFaceVertices = 4; /* Face has 4 vertices */
3602       break;
3603     case 8: /* hexahedron */
3604       *numFaceVertices = 4; /* Face has 4 vertices */
3605       break;
3606     case 9: /* tet cohesive Lagrange cells */
3607       *numFaceVertices = 6; /* Face has 6 vertices */
3608       break;
3609     case 10: /* quadratic tetrahedron */
3610       *numFaceVertices = 6; /* Face has 6 vertices */
3611       break;
3612     case 12: /* hex cohesive Lagrange cells */
3613       *numFaceVertices = 6; /* Face has 6 vertices */
3614       break;
3615     case 18: /* quadratic tet cohesive Lagrange cells */
3616       *numFaceVertices = 6; /* Face has 6 vertices */
3617       break;
3618     case 27: /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
3619       *numFaceVertices = 9; /* Face has 9 vertices */
3620       break;
3621     default:
3622       SETERRQ2(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %D for dimension %D", numCorners, cellDim);
3623     }
3624     break;
3625   default:
3626     SETERRQ1(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %D", cellDim);
3627   }
3628   PetscFunctionReturn(0);
3629 }
3630 
3631 /*@
3632   DMPlexGetDepthLabel - Get the DMLabel recording the depth of each point
3633 
3634   Not Collective
3635 
3636   Input Parameter:
3637 . dm    - The DMPlex object
3638 
3639   Output Parameter:
3640 . depthLabel - The DMLabel recording point depth
3641 
3642   Level: developer
3643 
3644 .seealso: DMPlexGetDepth(), DMPlexGetHeightStratum(), DMPlexGetDepthStratum(), DMPlexGetPointDepth(),
3645 @*/
3646 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
3647 {
3648   PetscFunctionBegin;
3649   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3650   PetscValidPointer(depthLabel, 2);
3651   *depthLabel = dm->depthLabel;
3652   PetscFunctionReturn(0);
3653 }
3654 
3655 /*@
3656   DMPlexGetDepth - Get the depth of the DAG representing this mesh
3657 
3658   Not Collective
3659 
3660   Input Parameter:
3661 . dm    - The DMPlex object
3662 
3663   Output Parameter:
3664 . depth - The number of strata (breadth first levels) in the DAG
3665 
3666   Level: developer
3667 
3668   Notes:
3669   This returns maximum of point depths over all points, i.e. maximum value of the label returned by DMPlexGetDepthLabel().
3670   The point depth is described more in detail in DMPlexGetDepthStratum().
3671   An empty mesh gives -1.
3672 
3673 .seealso: DMPlexGetDepthLabel(), DMPlexGetDepthStratum(), DMPlexGetPointDepth(), DMPlexSymmetrize()
3674 @*/
3675 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
3676 {
3677   DMLabel        label;
3678   PetscInt       d = 0;
3679   PetscErrorCode ierr;
3680 
3681   PetscFunctionBegin;
3682   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3683   PetscValidPointer(depth, 2);
3684   ierr = DMPlexGetDepthLabel(dm, &label);CHKERRQ(ierr);
3685   if (label) {ierr = DMLabelGetNumValues(label, &d);CHKERRQ(ierr);}
3686   *depth = d-1;
3687   PetscFunctionReturn(0);
3688 }
3689 
3690 /*@
3691   DMPlexGetDepthStratum - Get the bounds [start, end) for all points at a certain depth.
3692 
3693   Not Collective
3694 
3695   Input Parameters:
3696 + dm           - The DMPlex object
3697 - stratumValue - The requested depth
3698 
3699   Output Parameters:
3700 + start - The first point at this depth
3701 - end   - One beyond the last point at this depth
3702 
3703   Notes:
3704   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
3705   often "vertices".  If the mesh is "interpolated" (see DMPlexInterpolate()), then depth stratum 1 contains the next
3706   higher dimension, e.g., "edges".
3707 
3708   Level: developer
3709 
3710 .seealso: DMPlexGetHeightStratum(), DMPlexGetDepth(), DMPlexGetDepthLabel(), DMPlexGetPointDepth(), DMPlexSymmetrize(), DMPlexInterpolate()
3711 @*/
3712 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt stratumValue, PetscInt *start, PetscInt *end)
3713 {
3714   DMLabel        label;
3715   PetscInt       pStart, pEnd;
3716   PetscErrorCode ierr;
3717 
3718   PetscFunctionBegin;
3719   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3720   if (start) {PetscValidPointer(start, 3); *start = 0;}
3721   if (end)   {PetscValidPointer(end,   4); *end   = 0;}
3722   ierr = DMPlexGetChart(dm, &pStart, &pEnd);CHKERRQ(ierr);
3723   if (pStart == pEnd) PetscFunctionReturn(0);
3724   if (stratumValue < 0) {
3725     if (start) *start = pStart;
3726     if (end)   *end   = pEnd;
3727     PetscFunctionReturn(0);
3728   }
3729   ierr = DMPlexGetDepthLabel(dm, &label);CHKERRQ(ierr);
3730   if (!label) SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
3731   ierr = DMLabelGetStratumBounds(label, stratumValue, start, end);CHKERRQ(ierr);
3732   PetscFunctionReturn(0);
3733 }
3734 
3735 /*@
3736   DMPlexGetHeightStratum - Get the bounds [start, end) for all points at a certain height.
3737 
3738   Not Collective
3739 
3740   Input Parameters:
3741 + dm           - The DMPlex object
3742 - stratumValue - The requested height
3743 
3744   Output Parameters:
3745 + start - The first point at this height
3746 - end   - One beyond the last point at this height
3747 
3748   Notes:
3749   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
3750   points, often called "cells" or "elements".  If the mesh is "interpolated" (see DMPlexInterpolate()), then height
3751   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
3752 
3753   Level: developer
3754 
3755 .seealso: DMPlexGetDepthStratum(), DMPlexGetDepth(), DMPlexGetPointHeight()
3756 @*/
3757 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt stratumValue, PetscInt *start, PetscInt *end)
3758 {
3759   DMLabel        label;
3760   PetscInt       depth, pStart, pEnd;
3761   PetscErrorCode ierr;
3762 
3763   PetscFunctionBegin;
3764   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3765   if (start) {PetscValidPointer(start, 3); *start = 0;}
3766   if (end)   {PetscValidPointer(end,   4); *end   = 0;}
3767   ierr = DMPlexGetChart(dm, &pStart, &pEnd);CHKERRQ(ierr);
3768   if (pStart == pEnd) PetscFunctionReturn(0);
3769   if (stratumValue < 0) {
3770     if (start) *start = pStart;
3771     if (end)   *end   = pEnd;
3772     PetscFunctionReturn(0);
3773   }
3774   ierr = DMPlexGetDepthLabel(dm, &label);CHKERRQ(ierr);
3775   if (!label) SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
3776   ierr = DMLabelGetNumValues(label, &depth);CHKERRQ(ierr);
3777   ierr = DMLabelGetStratumBounds(label, depth-1-stratumValue, start, end);CHKERRQ(ierr);
3778   PetscFunctionReturn(0);
3779 }
3780 
3781 /*@
3782   DMPlexGetPointDepth - Get the depth of a given point
3783 
3784   Not Collective
3785 
3786   Input Parameter:
3787 + dm    - The DMPlex object
3788 - point - The point
3789 
3790   Output Parameter:
3791 . depth - The depth of the point
3792 
3793   Level: intermediate
3794 
3795 .seealso: DMPlexGetCellType(), DMPlexGetDepthLabel(), DMPlexGetDepth(), DMPlexGetPointHeight()
3796 @*/
3797 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
3798 {
3799   PetscErrorCode ierr;
3800 
3801   PetscFunctionBegin;
3802   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3803   PetscValidIntPointer(depth, 3);
3804   ierr = DMLabelGetValue(dm->depthLabel, point, depth);CHKERRQ(ierr);
3805   PetscFunctionReturn(0);
3806 }
3807 
3808 /*@
3809   DMPlexGetPointHeight - Get the height of a given point
3810 
3811   Not Collective
3812 
3813   Input Parameter:
3814 + dm    - The DMPlex object
3815 - point - The point
3816 
3817   Output Parameter:
3818 . height - The height of the point
3819 
3820   Level: intermediate
3821 
3822 .seealso: DMPlexGetCellType(), DMPlexGetDepthLabel(), DMPlexGetDepth(), DMPlexGetPointDepth()
3823 @*/
3824 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
3825 {
3826   PetscInt       n, pDepth;
3827   PetscErrorCode ierr;
3828 
3829   PetscFunctionBegin;
3830   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3831   PetscValidIntPointer(height, 3);
3832   ierr = DMLabelGetNumValues(dm->depthLabel, &n);CHKERRQ(ierr);
3833   ierr = DMLabelGetValue(dm->depthLabel, point, &pDepth);CHKERRQ(ierr);
3834   *height = n - 1 - pDepth;  /* DAG depth is n-1 */
3835   PetscFunctionReturn(0);
3836 }
3837 
3838 /*@
3839   DMPlexGetCellTypeLabel - Get the DMLabel recording the polytope type of each cell
3840 
3841   Not Collective
3842 
3843   Input Parameter:
3844 . dm - The DMPlex object
3845 
3846   Output Parameter:
3847 . celltypeLabel - The DMLabel recording cell polytope type
3848 
3849   Note: This function will trigger automatica computation of cell types. This can be disabled by calling
3850   DMCreateLabel(dm, "celltype") beforehand.
3851 
3852   Level: developer
3853 
3854 .seealso: DMPlexGetCellType(), DMPlexGetDepthLabel(), DMCreateLabel()
3855 @*/
3856 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
3857 {
3858   PetscErrorCode ierr;
3859 
3860   PetscFunctionBegin;
3861   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3862   PetscValidPointer(celltypeLabel, 2);
3863   if (!dm->celltypeLabel) {ierr = DMPlexComputeCellTypes(dm);CHKERRQ(ierr);}
3864   *celltypeLabel = dm->celltypeLabel;
3865   PetscFunctionReturn(0);
3866 }
3867 
3868 /*@
3869   DMPlexGetCellType - Get the polytope type of a given cell
3870 
3871   Not Collective
3872 
3873   Input Parameter:
3874 + dm   - The DMPlex object
3875 - cell - The cell
3876 
3877   Output Parameter:
3878 . celltype - The polytope type of the cell
3879 
3880   Level: intermediate
3881 
3882 .seealso: DMPlexGetCellTypeLabel(), DMPlexGetDepthLabel(), DMPlexGetDepth()
3883 @*/
3884 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
3885 {
3886   DMLabel        label;
3887   PetscInt       ct;
3888   PetscErrorCode ierr;
3889 
3890   PetscFunctionBegin;
3891   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3892   PetscValidPointer(celltype, 3);
3893   ierr = DMPlexGetCellTypeLabel(dm, &label);CHKERRQ(ierr);
3894   ierr = DMLabelGetValue(label, cell, &ct);CHKERRQ(ierr);
3895   *celltype = (DMPolytopeType) ct;
3896   PetscFunctionReturn(0);
3897 }
3898 
3899 /*@
3900   DMPlexSetCellType - Set the polytope type of a given cell
3901 
3902   Not Collective
3903 
3904   Input Parameters:
3905 + dm   - The DMPlex object
3906 . cell - The cell
3907 - celltype - The polytope type of the cell
3908 
3909   Note: By default, cell types will be automatically computed using DMPlexComputeCellTypes() before this function
3910   is executed. This function will override the computed type. However, if automatic classification will not succeed
3911   and a user wants to manually specify all types, the classification must be disabled by calling
3912   DMCreaateLabel(dm, "celltype") before getting or setting any cell types.
3913 
3914   Level: advanced
3915 
3916 .seealso: DMPlexGetCellTypeLabel(), DMPlexGetDepthLabel(), DMPlexGetDepth(), DMPlexComputeCellTypes(), DMCreateLabel()
3917 @*/
3918 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
3919 {
3920   DMLabel        label;
3921   PetscErrorCode ierr;
3922 
3923   PetscFunctionBegin;
3924   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3925   ierr = DMPlexGetCellTypeLabel(dm, &label);CHKERRQ(ierr);
3926   ierr = DMLabelSetValue(label, cell, celltype);CHKERRQ(ierr);
3927   PetscFunctionReturn(0);
3928 }
3929 
3930 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
3931 {
3932   PetscSection   section, s;
3933   Mat            m;
3934   PetscInt       maxHeight;
3935   PetscErrorCode ierr;
3936 
3937   PetscFunctionBegin;
3938   ierr = DMClone(dm, cdm);CHKERRQ(ierr);
3939   ierr = DMPlexGetMaxProjectionHeight(dm, &maxHeight);CHKERRQ(ierr);
3940   ierr = DMPlexSetMaxProjectionHeight(*cdm, maxHeight);CHKERRQ(ierr);
3941   ierr = PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section);CHKERRQ(ierr);
3942   ierr = DMSetLocalSection(*cdm, section);CHKERRQ(ierr);
3943   ierr = PetscSectionDestroy(&section);CHKERRQ(ierr);
3944   ierr = PetscSectionCreate(PETSC_COMM_SELF, &s);CHKERRQ(ierr);
3945   ierr = MatCreate(PETSC_COMM_SELF, &m);CHKERRQ(ierr);
3946   ierr = DMSetDefaultConstraints(*cdm, s, m);CHKERRQ(ierr);
3947   ierr = PetscSectionDestroy(&s);CHKERRQ(ierr);
3948   ierr = MatDestroy(&m);CHKERRQ(ierr);
3949 
3950   ierr = DMSetNumFields(*cdm, 1);CHKERRQ(ierr);
3951   ierr = DMCreateDS(*cdm);CHKERRQ(ierr);
3952   PetscFunctionReturn(0);
3953 }
3954 
3955 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
3956 {
3957   Vec            coordsLocal;
3958   DM             coordsDM;
3959   PetscErrorCode ierr;
3960 
3961   PetscFunctionBegin;
3962   *field = NULL;
3963   ierr = DMGetCoordinatesLocal(dm,&coordsLocal);CHKERRQ(ierr);
3964   ierr = DMGetCoordinateDM(dm,&coordsDM);CHKERRQ(ierr);
3965   if (coordsLocal && coordsDM) {
3966     ierr = DMFieldCreateDS(coordsDM, 0, coordsLocal, field);CHKERRQ(ierr);
3967   }
3968   PetscFunctionReturn(0);
3969 }
3970 
3971 /*@C
3972   DMPlexGetConeSection - Return a section which describes the layout of cone data
3973 
3974   Not Collective
3975 
3976   Input Parameters:
3977 . dm        - The DMPlex object
3978 
3979   Output Parameter:
3980 . section - The PetscSection object
3981 
3982   Level: developer
3983 
3984 .seealso: DMPlexGetSupportSection(), DMPlexGetCones(), DMPlexGetConeOrientations()
3985 @*/
3986 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
3987 {
3988   DM_Plex *mesh = (DM_Plex*) dm->data;
3989 
3990   PetscFunctionBegin;
3991   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3992   if (section) *section = mesh->coneSection;
3993   PetscFunctionReturn(0);
3994 }
3995 
3996 /*@C
3997   DMPlexGetSupportSection - Return a section which describes the layout of support data
3998 
3999   Not Collective
4000 
4001   Input Parameters:
4002 . dm        - The DMPlex object
4003 
4004   Output Parameter:
4005 . section - The PetscSection object
4006 
4007   Level: developer
4008 
4009 .seealso: DMPlexGetConeSection()
4010 @*/
4011 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
4012 {
4013   DM_Plex *mesh = (DM_Plex*) dm->data;
4014 
4015   PetscFunctionBegin;
4016   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4017   if (section) *section = mesh->supportSection;
4018   PetscFunctionReturn(0);
4019 }
4020 
4021 /*@C
4022   DMPlexGetCones - Return cone data
4023 
4024   Not Collective
4025 
4026   Input Parameters:
4027 . dm        - The DMPlex object
4028 
4029   Output Parameter:
4030 . cones - The cone for each point
4031 
4032   Level: developer
4033 
4034 .seealso: DMPlexGetConeSection()
4035 @*/
4036 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
4037 {
4038   DM_Plex *mesh = (DM_Plex*) dm->data;
4039 
4040   PetscFunctionBegin;
4041   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4042   if (cones) *cones = mesh->cones;
4043   PetscFunctionReturn(0);
4044 }
4045 
4046 /*@C
4047   DMPlexGetConeOrientations - Return cone orientation data
4048 
4049   Not Collective
4050 
4051   Input Parameters:
4052 . dm        - The DMPlex object
4053 
4054   Output Parameter:
4055 . coneOrientations - The cone orientation for each point
4056 
4057   Level: developer
4058 
4059 .seealso: DMPlexGetConeSection()
4060 @*/
4061 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
4062 {
4063   DM_Plex *mesh = (DM_Plex*) dm->data;
4064 
4065   PetscFunctionBegin;
4066   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4067   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
4068   PetscFunctionReturn(0);
4069 }
4070 
4071 /******************************** FEM Support **********************************/
4072 
4073 /*
4074  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
4075  representing a line in the section.
4076 */
4077 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(PetscSection section,PetscInt field,PetscInt line,PetscBool vertexchart,PetscInt *Nc,PetscInt *k)
4078 {
4079   PetscErrorCode ierr;
4080 
4081   PetscFunctionBeginHot;
4082   ierr = PetscSectionGetFieldComponents(section, field, Nc);CHKERRQ(ierr);
4083   if (line < 0) {
4084     *k = 0;
4085     *Nc = 0;
4086   } else if (vertexchart) {            /* If we only have a vertex chart, we must have degree k=1 */
4087     *k = 1;
4088   } else {                      /* Assume the full interpolated mesh is in the chart; lines in particular */
4089     /* An order k SEM disc has k-1 dofs on an edge */
4090     ierr = PetscSectionGetFieldDof(section, line, field, k);CHKERRQ(ierr);
4091     *k = *k / *Nc + 1;
4092   }
4093   PetscFunctionReturn(0);
4094 }
4095 
4096 /*@
4097 
4098   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
4099   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
4100   section provided (or the section of the DM).
4101 
4102   Input Parameters:
4103 + dm      - The DM
4104 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or PETSC_DETERMINE
4105 - section - The PetscSection to reorder, or NULL for the default section
4106 
4107   Note: The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
4108   degree of the basis.
4109 
4110   Example:
4111   A typical interpolated single-quad mesh might order points as
4112 .vb
4113   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
4114 
4115   v4 -- e6 -- v3
4116   |           |
4117   e7    c0    e8
4118   |           |
4119   v1 -- e5 -- v2
4120 .ve
4121 
4122   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
4123   dofs in the order of points, e.g.,
4124 .vb
4125     c0 -> [0,1,2,3]
4126     v1 -> [4]
4127     ...
4128     e5 -> [8, 9]
4129 .ve
4130 
4131   which corresponds to the dofs
4132 .vb
4133     6   10  11  7
4134     13  2   3   15
4135     12  0   1   14
4136     4   8   9   5
4137 .ve
4138 
4139   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
4140 .vb
4141   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
4142 .ve
4143 
4144   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
4145 .vb
4146    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
4147 .ve
4148 
4149   Level: developer
4150 
4151 .seealso: DMGetLocalSection(), PetscSectionSetClosurePermutation(), DMSetGlobalSection()
4152 @*/
4153 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
4154 {
4155   DMLabel        label;
4156   PetscInt      *perm;
4157   PetscInt       dim, depth = -1, eStart = -1, k, Nf, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
4158   PetscBool      vertexchart;
4159   PetscErrorCode ierr;
4160 
4161   PetscFunctionBegin;
4162   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
4163   if (dim < 1) PetscFunctionReturn(0);
4164   if (point < 0) {
4165     PetscInt sStart,sEnd;
4166 
4167     ierr = DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd);CHKERRQ(ierr);
4168     point = sEnd-sStart ? sStart : point;
4169   }
4170   ierr = DMPlexGetDepthLabel(dm, &label);CHKERRQ(ierr);
4171   if (point >= 0) { ierr = DMLabelGetValue(label, point, &depth);CHKERRQ(ierr); }
4172   if (!section) {ierr = DMGetLocalSection(dm, &section);CHKERRQ(ierr);}
4173   if (depth == 1) {eStart = point;}
4174   else if  (depth == dim) {
4175     const PetscInt *cone;
4176 
4177     ierr = DMPlexGetCone(dm, point, &cone);CHKERRQ(ierr);
4178     if (dim == 2) eStart = cone[0];
4179     else if (dim == 3) {
4180       const PetscInt *cone2;
4181       ierr = DMPlexGetCone(dm, cone[0], &cone2);CHKERRQ(ierr);
4182       eStart = cone2[0];
4183     } else SETERRQ3(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Point %D of depth %D cannot be used to bootstrap spectral ordering for dim %D", point, depth, dim);
4184   } else if (depth >= 0) SETERRQ3(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Point %D of depth %D cannot be used to bootstrap spectral ordering for dim %D", point, depth, dim);
4185   {                             /* Determine whether the chart covers all points or just vertices. */
4186     PetscInt pStart,pEnd,cStart,cEnd;
4187     ierr = DMPlexGetDepthStratum(dm,0,&pStart,&pEnd);CHKERRQ(ierr);
4188     ierr = PetscSectionGetChart(section,&cStart,&cEnd);CHKERRQ(ierr);
4189     if (pStart == cStart && pEnd == cEnd) vertexchart = PETSC_TRUE; /* Just vertices */
4190     else vertexchart = PETSC_FALSE;                                 /* Assume all interpolated points are in chart */
4191   }
4192   ierr = PetscSectionGetNumFields(section, &Nf);CHKERRQ(ierr);
4193   for (f = 0; f < Nf; ++f) {
4194     ierr = PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k);CHKERRQ(ierr);
4195     size += PetscPowInt(k+1, dim)*Nc;
4196   }
4197   ierr = PetscMalloc1(size, &perm);CHKERRQ(ierr);
4198   for (f = 0; f < Nf; ++f) {
4199     switch (dim) {
4200     case 1:
4201       ierr = PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k);CHKERRQ(ierr);
4202       /*
4203         Original ordering is [ edge of length k-1; vtx0; vtx1 ]
4204         We want              [ vtx0; edge of length k-1; vtx1 ]
4205       */
4206       for (c=0; c<Nc; c++,offset++) perm[offset] = (k-1)*Nc + c + foffset;
4207       for (i=0; i<k-1; i++) for (c=0; c<Nc; c++,offset++) perm[offset] = i*Nc + c + foffset;
4208       for (c=0; c<Nc; c++,offset++) perm[offset] = k*Nc + c + foffset;
4209       foffset = offset;
4210       break;
4211     case 2:
4212       /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
4213       ierr = PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k);CHKERRQ(ierr);
4214       /* The SEM order is
4215 
4216          v_lb, {e_b}, v_rb,
4217          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
4218          v_lt, reverse {e_t}, v_rt
4219       */
4220       {
4221         const PetscInt of   = 0;
4222         const PetscInt oeb  = of   + PetscSqr(k-1);
4223         const PetscInt oer  = oeb  + (k-1);
4224         const PetscInt oet  = oer  + (k-1);
4225         const PetscInt oel  = oet  + (k-1);
4226         const PetscInt ovlb = oel  + (k-1);
4227         const PetscInt ovrb = ovlb + 1;
4228         const PetscInt ovrt = ovrb + 1;
4229         const PetscInt ovlt = ovrt + 1;
4230         PetscInt       o;
4231 
4232         /* bottom */
4233         for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb*Nc + c + foffset;
4234         for (o = oeb; o < oer; ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
4235         for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb*Nc + c + foffset;
4236         /* middle */
4237         for (i = 0; i < k-1; ++i) {
4238           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel+(k-2)-i)*Nc + c + foffset;
4239           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;
4240           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer+i)*Nc + c + foffset;
4241         }
4242         /* top */
4243         for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt*Nc + c + foffset;
4244         for (o = oel-1; o >= oet; --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
4245         for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt*Nc + c + foffset;
4246         foffset = offset;
4247       }
4248       break;
4249     case 3:
4250       /* The original hex closure is
4251 
4252          {c,
4253           f_b, f_t, f_f, f_b, f_r, f_l,
4254           e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
4255           v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
4256       */
4257       ierr = PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k);CHKERRQ(ierr);
4258       /* The SEM order is
4259          Bottom Slice
4260          v_blf, {e^{(k-1)-n}_bf}, v_brf,
4261          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
4262          v_blb, {e_bb}, v_brb,
4263 
4264          Middle Slice (j)
4265          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
4266          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
4267          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
4268 
4269          Top Slice
4270          v_tlf, {e_tf}, v_trf,
4271          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
4272          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
4273       */
4274       {
4275         const PetscInt oc    = 0;
4276         const PetscInt ofb   = oc    + PetscSqr(k-1)*(k-1);
4277         const PetscInt oft   = ofb   + PetscSqr(k-1);
4278         const PetscInt off   = oft   + PetscSqr(k-1);
4279         const PetscInt ofk   = off   + PetscSqr(k-1);
4280         const PetscInt ofr   = ofk   + PetscSqr(k-1);
4281         const PetscInt ofl   = ofr   + PetscSqr(k-1);
4282         const PetscInt oebl  = ofl   + PetscSqr(k-1);
4283         const PetscInt oebb  = oebl  + (k-1);
4284         const PetscInt oebr  = oebb  + (k-1);
4285         const PetscInt oebf  = oebr  + (k-1);
4286         const PetscInt oetf  = oebf  + (k-1);
4287         const PetscInt oetr  = oetf  + (k-1);
4288         const PetscInt oetb  = oetr  + (k-1);
4289         const PetscInt oetl  = oetb  + (k-1);
4290         const PetscInt oerf  = oetl  + (k-1);
4291         const PetscInt oelf  = oerf  + (k-1);
4292         const PetscInt oelb  = oelf  + (k-1);
4293         const PetscInt oerb  = oelb  + (k-1);
4294         const PetscInt ovblf = oerb  + (k-1);
4295         const PetscInt ovblb = ovblf + 1;
4296         const PetscInt ovbrb = ovblb + 1;
4297         const PetscInt ovbrf = ovbrb + 1;
4298         const PetscInt ovtlf = ovbrf + 1;
4299         const PetscInt ovtrf = ovtlf + 1;
4300         const PetscInt ovtrb = ovtrf + 1;
4301         const PetscInt ovtlb = ovtrb + 1;
4302         PetscInt       o, n;
4303 
4304         /* Bottom Slice */
4305         /*   bottom */
4306         for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf*Nc + c + foffset;
4307         for (o = oetf-1; o >= oebf; --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
4308         for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf*Nc + c + foffset;
4309         /*   middle */
4310         for (i = 0; i < k-1; ++i) {
4311           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl+i)*Nc + c + foffset;
4312           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;}
4313           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr+(k-2)-i)*Nc + c + foffset;
4314         }
4315         /*   top */
4316         for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb*Nc + c + foffset;
4317         for (o = oebb; o < oebr; ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
4318         for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb*Nc + c + foffset;
4319 
4320         /* Middle Slice */
4321         for (j = 0; j < k-1; ++j) {
4322           /*   bottom */
4323           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf+(k-2)-j)*Nc + c + foffset;
4324           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;
4325           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf+j)*Nc + c + foffset;
4326           /*   middle */
4327           for (i = 0; i < k-1; ++i) {
4328             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl+i*(k-1)+j)*Nc + c + foffset;
4329             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;
4330             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr+j*(k-1)+i)*Nc + c + foffset;
4331           }
4332           /*   top */
4333           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb+j)*Nc + c + foffset;
4334           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;
4335           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb+(k-2)-j)*Nc + c + foffset;
4336         }
4337 
4338         /* Top Slice */
4339         /*   bottom */
4340         for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf*Nc + c + foffset;
4341         for (o = oetf; o < oetr; ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
4342         for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf*Nc + c + foffset;
4343         /*   middle */
4344         for (i = 0; i < k-1; ++i) {
4345           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl+(k-2)-i)*Nc + c + foffset;
4346           for (n = 0; n < k-1; ++n) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft+i*(k-1)+n)*Nc + c + foffset;
4347           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr+i)*Nc + c + foffset;
4348         }
4349         /*   top */
4350         for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb*Nc + c + foffset;
4351         for (o = oetl-1; o >= oetb; --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
4352         for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb*Nc + c + foffset;
4353 
4354         foffset = offset;
4355       }
4356       break;
4357     default: SETERRQ1(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %D", dim);
4358     }
4359   }
4360   if (offset != size) SETERRQ2(PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Number of permutation entries %D != %D", offset, size);
4361   /* Check permutation */
4362   {
4363     PetscInt *check;
4364 
4365     ierr = PetscMalloc1(size, &check);CHKERRQ(ierr);
4366     for (i = 0; i < size; ++i) {check[i] = -1; if (perm[i] < 0 || perm[i] >= size) SETERRQ2(PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Invalid permutation index p[%D] = %D", i, perm[i]);}
4367     for (i = 0; i < size; ++i) check[perm[i]] = i;
4368     for (i = 0; i < size; ++i) {if (check[i] < 0) SETERRQ1(PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Missing permutation index %D", i);}
4369     ierr = PetscFree(check);CHKERRQ(ierr);
4370   }
4371   ierr = PetscSectionSetClosurePermutation_Internal(section, (PetscObject) dm, size, PETSC_OWN_POINTER, perm);CHKERRQ(ierr);
4372   PetscFunctionReturn(0);
4373 }
4374 
4375 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
4376 {
4377   PetscDS        prob;
4378   PetscInt       depth, Nf, h;
4379   DMLabel        label;
4380   PetscErrorCode ierr;
4381 
4382   PetscFunctionBeginHot;
4383   ierr = DMGetDS(dm, &prob);CHKERRQ(ierr);
4384   Nf      = prob->Nf;
4385   label   = dm->depthLabel;
4386   *dspace = NULL;
4387   if (field < Nf) {
4388     PetscObject disc = prob->disc[field];
4389 
4390     if (disc->classid == PETSCFE_CLASSID) {
4391       PetscDualSpace dsp;
4392 
4393       ierr = PetscFEGetDualSpace((PetscFE)disc,&dsp);CHKERRQ(ierr);
4394       ierr = DMLabelGetNumValues(label,&depth);CHKERRQ(ierr);
4395       ierr = DMLabelGetValue(label,point,&h);CHKERRQ(ierr);
4396       h    = depth - 1 - h;
4397       if (h) {
4398         ierr = PetscDualSpaceGetHeightSubspace(dsp,h,dspace);CHKERRQ(ierr);
4399       } else {
4400         *dspace = dsp;
4401       }
4402     }
4403   }
4404   PetscFunctionReturn(0);
4405 }
4406 
4407 
4408 PETSC_STATIC_INLINE PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
4409 {
4410   PetscScalar    *array, *vArray;
4411   const PetscInt *cone, *coneO;
4412   PetscInt        pStart, pEnd, p, numPoints, size = 0, offset = 0;
4413   PetscErrorCode  ierr;
4414 
4415   PetscFunctionBeginHot;
4416   ierr = PetscSectionGetChart(section, &pStart, &pEnd);CHKERRQ(ierr);
4417   ierr = DMPlexGetConeSize(dm, point, &numPoints);CHKERRQ(ierr);
4418   ierr = DMPlexGetCone(dm, point, &cone);CHKERRQ(ierr);
4419   ierr = DMPlexGetConeOrientation(dm, point, &coneO);CHKERRQ(ierr);
4420   if (!values || !*values) {
4421     if ((point >= pStart) && (point < pEnd)) {
4422       PetscInt dof;
4423 
4424       ierr = PetscSectionGetDof(section, point, &dof);CHKERRQ(ierr);
4425       size += dof;
4426     }
4427     for (p = 0; p < numPoints; ++p) {
4428       const PetscInt cp = cone[p];
4429       PetscInt       dof;
4430 
4431       if ((cp < pStart) || (cp >= pEnd)) continue;
4432       ierr = PetscSectionGetDof(section, cp, &dof);CHKERRQ(ierr);
4433       size += dof;
4434     }
4435     if (!values) {
4436       if (csize) *csize = size;
4437       PetscFunctionReturn(0);
4438     }
4439     ierr = DMGetWorkArray(dm, size, MPIU_SCALAR, &array);CHKERRQ(ierr);
4440   } else {
4441     array = *values;
4442   }
4443   size = 0;
4444   ierr = VecGetArray(v, &vArray);CHKERRQ(ierr);
4445   if ((point >= pStart) && (point < pEnd)) {
4446     PetscInt     dof, off, d;
4447     PetscScalar *varr;
4448 
4449     ierr = PetscSectionGetDof(section, point, &dof);CHKERRQ(ierr);
4450     ierr = PetscSectionGetOffset(section, point, &off);CHKERRQ(ierr);
4451     varr = &vArray[off];
4452     for (d = 0; d < dof; ++d, ++offset) {
4453       array[offset] = varr[d];
4454     }
4455     size += dof;
4456   }
4457   for (p = 0; p < numPoints; ++p) {
4458     const PetscInt cp = cone[p];
4459     PetscInt       o  = coneO[p];
4460     PetscInt       dof, off, d;
4461     PetscScalar   *varr;
4462 
4463     if ((cp < pStart) || (cp >= pEnd)) continue;
4464     ierr = PetscSectionGetDof(section, cp, &dof);CHKERRQ(ierr);
4465     ierr = PetscSectionGetOffset(section, cp, &off);CHKERRQ(ierr);
4466     varr = &vArray[off];
4467     if (o >= 0) {
4468       for (d = 0; d < dof; ++d, ++offset) {
4469         array[offset] = varr[d];
4470       }
4471     } else {
4472       for (d = dof-1; d >= 0; --d, ++offset) {
4473         array[offset] = varr[d];
4474       }
4475     }
4476     size += dof;
4477   }
4478   ierr = VecRestoreArray(v, &vArray);CHKERRQ(ierr);
4479   if (!*values) {
4480     if (csize) *csize = size;
4481     *values = array;
4482   } else {
4483     if (size > *csize) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %D < actual size %D", *csize, size);
4484     *csize = size;
4485   }
4486   PetscFunctionReturn(0);
4487 }
4488 
4489 /* Compressed closure does not apply closure permutation */
4490 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
4491 {
4492   const PetscInt *cla;
4493   PetscInt       np, *pts = NULL;
4494   PetscErrorCode ierr;
4495 
4496   PetscFunctionBeginHot;
4497   ierr = PetscSectionGetClosureIndex(section, (PetscObject) dm, clSec, clPoints);CHKERRQ(ierr);
4498   if (!*clPoints) {
4499     PetscInt pStart, pEnd, p, q;
4500 
4501     ierr = PetscSectionGetChart(section, &pStart, &pEnd);CHKERRQ(ierr);
4502     ierr = DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &np, &pts);CHKERRQ(ierr);
4503     /* Compress out points not in the section */
4504     for (p = 0, q = 0; p < np; p++) {
4505       PetscInt r = pts[2*p];
4506       if ((r >= pStart) && (r < pEnd)) {
4507         pts[q*2]   = r;
4508         pts[q*2+1] = pts[2*p+1];
4509         ++q;
4510       }
4511     }
4512     np = q;
4513     cla = NULL;
4514   } else {
4515     PetscInt dof, off;
4516 
4517     ierr = PetscSectionGetDof(*clSec, point, &dof);CHKERRQ(ierr);
4518     ierr = PetscSectionGetOffset(*clSec, point, &off);CHKERRQ(ierr);
4519     ierr = ISGetIndices(*clPoints, &cla);CHKERRQ(ierr);
4520     np   = dof/2;
4521     pts  = (PetscInt *) &cla[off];
4522   }
4523   *numPoints = np;
4524   *points    = pts;
4525   *clp       = cla;
4526 
4527   PetscFunctionReturn(0);
4528 }
4529 
4530 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
4531 {
4532   PetscErrorCode ierr;
4533 
4534   PetscFunctionBeginHot;
4535   if (!*clPoints) {
4536     ierr = DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points);CHKERRQ(ierr);
4537   } else {
4538     ierr = ISRestoreIndices(*clPoints, clp);CHKERRQ(ierr);
4539   }
4540   *numPoints = 0;
4541   *points    = NULL;
4542   *clSec     = NULL;
4543   *clPoints  = NULL;
4544   *clp       = NULL;
4545   PetscFunctionReturn(0);
4546 }
4547 
4548 PETSC_STATIC_INLINE PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
4549 {
4550   PetscInt          offset = 0, p;
4551   const PetscInt    **perms = NULL;
4552   const PetscScalar **flips = NULL;
4553   PetscErrorCode    ierr;
4554 
4555   PetscFunctionBeginHot;
4556   *size = 0;
4557   ierr = PetscSectionGetPointSyms(section,numPoints,points,&perms,&flips);CHKERRQ(ierr);
4558   for (p = 0; p < numPoints; p++) {
4559     const PetscInt    point = points[2*p];
4560     const PetscInt    *perm = perms ? perms[p] : NULL;
4561     const PetscScalar *flip = flips ? flips[p] : NULL;
4562     PetscInt          dof, off, d;
4563     const PetscScalar *varr;
4564 
4565     ierr = PetscSectionGetDof(section, point, &dof);CHKERRQ(ierr);
4566     ierr = PetscSectionGetOffset(section, point, &off);CHKERRQ(ierr);
4567     varr = &vArray[off];
4568     if (clperm) {
4569       if (perm) {
4570         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]]  = varr[d];
4571       } else {
4572         for (d = 0; d < dof; d++) array[clperm[offset +      d ]]  = varr[d];
4573       }
4574       if (flip) {
4575         for (d = 0; d < dof; d++) array[clperm[offset +      d ]] *= flip[d];
4576       }
4577     } else {
4578       if (perm) {
4579         for (d = 0; d < dof; d++) array[offset + perm[d]]  = varr[d];
4580       } else {
4581         for (d = 0; d < dof; d++) array[offset +      d ]  = varr[d];
4582       }
4583       if (flip) {
4584         for (d = 0; d < dof; d++) array[offset +      d ] *= flip[d];
4585       }
4586     }
4587     offset += dof;
4588   }
4589   ierr = PetscSectionRestorePointSyms(section,numPoints,points,&perms,&flips);CHKERRQ(ierr);
4590   *size = offset;
4591   PetscFunctionReturn(0);
4592 }
4593 
4594 PETSC_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[])
4595 {
4596   PetscInt          offset = 0, f;
4597   PetscErrorCode    ierr;
4598 
4599   PetscFunctionBeginHot;
4600   *size = 0;
4601   for (f = 0; f < numFields; ++f) {
4602     PetscInt          p;
4603     const PetscInt    **perms = NULL;
4604     const PetscScalar **flips = NULL;
4605 
4606     ierr = PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips);CHKERRQ(ierr);
4607     for (p = 0; p < numPoints; p++) {
4608       const PetscInt    point = points[2*p];
4609       PetscInt          fdof, foff, b;
4610       const PetscScalar *varr;
4611       const PetscInt    *perm = perms ? perms[p] : NULL;
4612       const PetscScalar *flip = flips ? flips[p] : NULL;
4613 
4614       ierr = PetscSectionGetFieldDof(section, point, f, &fdof);CHKERRQ(ierr);
4615       ierr = PetscSectionGetFieldOffset(section, point, f, &foff);CHKERRQ(ierr);
4616       varr = &vArray[foff];
4617       if (clperm) {
4618         if (perm) {for (b = 0; b < fdof; b++) {array[clperm[offset + perm[b]]]  = varr[b];}}
4619         else      {for (b = 0; b < fdof; b++) {array[clperm[offset +      b ]]  = varr[b];}}
4620         if (flip) {for (b = 0; b < fdof; b++) {array[clperm[offset +      b ]] *= flip[b];}}
4621       } else {
4622         if (perm) {for (b = 0; b < fdof; b++) {array[offset + perm[b]]  = varr[b];}}
4623         else      {for (b = 0; b < fdof; b++) {array[offset +      b ]  = varr[b];}}
4624         if (flip) {for (b = 0; b < fdof; b++) {array[offset +      b ] *= flip[b];}}
4625       }
4626       offset += fdof;
4627     }
4628     ierr = PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips);CHKERRQ(ierr);
4629   }
4630   *size = offset;
4631   PetscFunctionReturn(0);
4632 }
4633 
4634 /*@C
4635   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
4636 
4637   Not collective
4638 
4639   Input Parameters:
4640 + dm - The DM
4641 . section - The section describing the layout in v, or NULL to use the default section
4642 . v - The local vector
4643 . point - The point in the DM
4644 . csize - The size of the input values array, or NULL
4645 - values - An array to use for the values, or NULL to have it allocated automatically
4646 
4647   Output Parameters:
4648 + csize - The number of values in the closure
4649 - values - The array of values. If the user provided NULL, it is a borrowed array and should not be freed
4650 
4651 $ Note that DMPlexVecGetClosure/DMPlexVecRestoreClosure only allocates the values array if it set to NULL in the
4652 $ calling function. This is because DMPlexVecGetClosure() is typically called in the inner loop of a Vec or Mat
4653 $ assembly function, and a user may already have allocated storage for this operation.
4654 $
4655 $ A typical use could be
4656 $
4657 $  values = NULL;
4658 $  ierr = DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values);CHKERRQ(ierr);
4659 $  for (cl = 0; cl < clSize; ++cl) {
4660 $    <Compute on closure>
4661 $  }
4662 $  ierr = DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values);CHKERRQ(ierr);
4663 $
4664 $ or
4665 $
4666 $  PetscMalloc1(clMaxSize, &values);
4667 $  for (p = pStart; p < pEnd; ++p) {
4668 $    clSize = clMaxSize;
4669 $    ierr = DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values);CHKERRQ(ierr);
4670 $    for (cl = 0; cl < clSize; ++cl) {
4671 $      <Compute on closure>
4672 $    }
4673 $  }
4674 $  PetscFree(values);
4675 
4676   Fortran Notes:
4677   Since it returns an array, this routine is only available in Fortran 90, and you must
4678   include petsc.h90 in your code.
4679 
4680   The csize argument is not present in the Fortran 90 binding since it is internal to the array.
4681 
4682   Level: intermediate
4683 
4684 .seealso DMPlexVecRestoreClosure(), DMPlexVecSetClosure(), DMPlexMatSetClosure()
4685 @*/
4686 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
4687 {
4688   PetscSection       clSection;
4689   IS                 clPoints;
4690   PetscScalar       *array;
4691   const PetscScalar *vArray;
4692   PetscInt          *points = NULL;
4693   const PetscInt    *clp, *perm;
4694   PetscInt           depth, numFields, numPoints, size;
4695   PetscErrorCode     ierr;
4696 
4697   PetscFunctionBeginHot;
4698   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4699   if (!section) {ierr = DMGetLocalSection(dm, &section);CHKERRQ(ierr);}
4700   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
4701   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
4702   ierr = DMPlexGetDepth(dm, &depth);CHKERRQ(ierr);
4703   ierr = PetscSectionGetNumFields(section, &numFields);CHKERRQ(ierr);
4704   if (depth == 1 && numFields < 2) {
4705     ierr = DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values);CHKERRQ(ierr);
4706     PetscFunctionReturn(0);
4707   }
4708   /* Get points */
4709   ierr = DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp);CHKERRQ(ierr);
4710   ierr = PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, NULL, &perm);CHKERRQ(ierr);
4711   /* Get array */
4712   if (!values || !*values) {
4713     PetscInt asize = 0, dof, p;
4714 
4715     for (p = 0; p < numPoints*2; p += 2) {
4716       ierr = PetscSectionGetDof(section, points[p], &dof);CHKERRQ(ierr);
4717       asize += dof;
4718     }
4719     if (!values) {
4720       ierr = DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp);CHKERRQ(ierr);
4721       if (csize) *csize = asize;
4722       PetscFunctionReturn(0);
4723     }
4724     ierr = DMGetWorkArray(dm, asize, MPIU_SCALAR, &array);CHKERRQ(ierr);
4725   } else {
4726     array = *values;
4727   }
4728   ierr = VecGetArrayRead(v, &vArray);CHKERRQ(ierr);
4729   /* Get values */
4730   if (numFields > 0) {ierr = DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, array);CHKERRQ(ierr);}
4731   else               {ierr = DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, array);CHKERRQ(ierr);}
4732   /* Cleanup points */
4733   ierr = DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp);CHKERRQ(ierr);
4734   /* Cleanup array */
4735   ierr = VecRestoreArrayRead(v, &vArray);CHKERRQ(ierr);
4736   if (!*values) {
4737     if (csize) *csize = size;
4738     *values = array;
4739   } else {
4740     if (size > *csize) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %D < actual size %D", *csize, size);
4741     *csize = size;
4742   }
4743   PetscFunctionReturn(0);
4744 }
4745 
4746 /*@C
4747   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
4748 
4749   Not collective
4750 
4751   Input Parameters:
4752 + dm - The DM
4753 . section - The section describing the layout in v, or NULL to use the default section
4754 . v - The local vector
4755 . point - The point in the DM
4756 . csize - The number of values in the closure, or NULL
4757 - values - The array of values, which is a borrowed array and should not be freed
4758 
4759   Note that the array values are discarded and not copied back into v. In order to copy values back to v, use DMPlexVecSetClosure()
4760 
4761   Fortran Notes:
4762   Since it returns an array, this routine is only available in Fortran 90, and you must
4763   include petsc.h90 in your code.
4764 
4765   The csize argument is not present in the Fortran 90 binding since it is internal to the array.
4766 
4767   Level: intermediate
4768 
4769 .seealso DMPlexVecGetClosure(), DMPlexVecSetClosure(), DMPlexMatSetClosure()
4770 @*/
4771 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
4772 {
4773   PetscInt       size = 0;
4774   PetscErrorCode ierr;
4775 
4776   PetscFunctionBegin;
4777   /* Should work without recalculating size */
4778   ierr = DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void*) values);CHKERRQ(ierr);
4779   *values = NULL;
4780   PetscFunctionReturn(0);
4781 }
4782 
4783 PETSC_STATIC_INLINE void add   (PetscScalar *x, PetscScalar y) {*x += y;}
4784 PETSC_STATIC_INLINE void insert(PetscScalar *x, PetscScalar y) {*x  = y;}
4785 
4786 PETSC_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[])
4787 {
4788   PetscInt        cdof;   /* The number of constraints on this point */
4789   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
4790   PetscScalar    *a;
4791   PetscInt        off, cind = 0, k;
4792   PetscErrorCode  ierr;
4793 
4794   PetscFunctionBegin;
4795   ierr = PetscSectionGetConstraintDof(section, point, &cdof);CHKERRQ(ierr);
4796   ierr = PetscSectionGetOffset(section, point, &off);CHKERRQ(ierr);
4797   a    = &array[off];
4798   if (!cdof || setBC) {
4799     if (clperm) {
4800       if (perm) {for (k = 0; k < dof; ++k) {fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));}}
4801       else      {for (k = 0; k < dof; ++k) {fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));}}
4802     } else {
4803       if (perm) {for (k = 0; k < dof; ++k) {fuse(&a[k], values[offset+perm[k]] * (flip ? flip[perm[k]] : 1.));}}
4804       else      {for (k = 0; k < dof; ++k) {fuse(&a[k], values[offset+     k ] * (flip ? flip[     k ] : 1.));}}
4805     }
4806   } else {
4807     ierr = PetscSectionGetConstraintIndices(section, point, &cdofs);CHKERRQ(ierr);
4808     if (clperm) {
4809       if (perm) {for (k = 0; k < dof; ++k) {
4810           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
4811           fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));
4812         }
4813       } else {
4814         for (k = 0; k < dof; ++k) {
4815           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
4816           fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));
4817         }
4818       }
4819     } else {
4820       if (perm) {
4821         for (k = 0; k < dof; ++k) {
4822           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
4823           fuse(&a[k], values[offset+perm[k]] * (flip ? flip[perm[k]] : 1.));
4824         }
4825       } else {
4826         for (k = 0; k < dof; ++k) {
4827           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
4828           fuse(&a[k], values[offset+     k ] * (flip ? flip[     k ] : 1.));
4829         }
4830       }
4831     }
4832   }
4833   PetscFunctionReturn(0);
4834 }
4835 
4836 PETSC_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[])
4837 {
4838   PetscInt        cdof;   /* The number of constraints on this point */
4839   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
4840   PetscScalar    *a;
4841   PetscInt        off, cind = 0, k;
4842   PetscErrorCode  ierr;
4843 
4844   PetscFunctionBegin;
4845   ierr = PetscSectionGetConstraintDof(section, point, &cdof);CHKERRQ(ierr);
4846   ierr = PetscSectionGetOffset(section, point, &off);CHKERRQ(ierr);
4847   a    = &array[off];
4848   if (cdof) {
4849     ierr = PetscSectionGetConstraintIndices(section, point, &cdofs);CHKERRQ(ierr);
4850     if (clperm) {
4851       if (perm) {
4852         for (k = 0; k < dof; ++k) {
4853           if ((cind < cdof) && (k == cdofs[cind])) {
4854             fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));
4855             cind++;
4856           }
4857         }
4858       } else {
4859         for (k = 0; k < dof; ++k) {
4860           if ((cind < cdof) && (k == cdofs[cind])) {
4861             fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));
4862             cind++;
4863           }
4864         }
4865       }
4866     } else {
4867       if (perm) {
4868         for (k = 0; k < dof; ++k) {
4869           if ((cind < cdof) && (k == cdofs[cind])) {
4870             fuse(&a[k], values[offset+perm[k]] * (flip ? flip[perm[k]] : 1.));
4871             cind++;
4872           }
4873         }
4874       } else {
4875         for (k = 0; k < dof; ++k) {
4876           if ((cind < cdof) && (k == cdofs[cind])) {
4877             fuse(&a[k], values[offset+     k ] * (flip ? flip[     k ] : 1.));
4878             cind++;
4879           }
4880         }
4881       }
4882     }
4883   }
4884   PetscFunctionReturn(0);
4885 }
4886 
4887 PETSC_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[])
4888 {
4889   PetscScalar    *a;
4890   PetscInt        fdof, foff, fcdof, foffset = *offset;
4891   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
4892   PetscInt        cind = 0, b;
4893   PetscErrorCode  ierr;
4894 
4895   PetscFunctionBegin;
4896   ierr = PetscSectionGetFieldDof(section, point, f, &fdof);CHKERRQ(ierr);
4897   ierr = PetscSectionGetFieldConstraintDof(section, point, f, &fcdof);CHKERRQ(ierr);
4898   ierr = PetscSectionGetFieldOffset(section, point, f, &foff);CHKERRQ(ierr);
4899   a    = &array[foff];
4900   if (!fcdof || setBC) {
4901     if (clperm) {
4902       if (perm) {for (b = 0; b < fdof; b++) {fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));}}
4903       else      {for (b = 0; b < fdof; b++) {fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));}}
4904     } else {
4905       if (perm) {for (b = 0; b < fdof; b++) {fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));}}
4906       else      {for (b = 0; b < fdof; b++) {fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));}}
4907     }
4908   } else {
4909     ierr = PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs);CHKERRQ(ierr);
4910     if (clperm) {
4911       if (perm) {
4912         for (b = 0; b < fdof; b++) {
4913           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
4914           fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));
4915         }
4916       } else {
4917         for (b = 0; b < fdof; b++) {
4918           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
4919           fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));
4920         }
4921       }
4922     } else {
4923       if (perm) {
4924         for (b = 0; b < fdof; b++) {
4925           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
4926           fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));
4927         }
4928       } else {
4929         for (b = 0; b < fdof; b++) {
4930           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
4931           fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));
4932         }
4933       }
4934     }
4935   }
4936   *offset += fdof;
4937   PetscFunctionReturn(0);
4938 }
4939 
4940 PETSC_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[])
4941 {
4942   PetscScalar    *a;
4943   PetscInt        fdof, foff, fcdof, foffset = *offset;
4944   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
4945   PetscInt        Nc, cind = 0, ncind = 0, b;
4946   PetscBool       ncSet, fcSet;
4947   PetscErrorCode  ierr;
4948 
4949   PetscFunctionBegin;
4950   ierr = PetscSectionGetFieldComponents(section, f, &Nc);CHKERRQ(ierr);
4951   ierr = PetscSectionGetFieldDof(section, point, f, &fdof);CHKERRQ(ierr);
4952   ierr = PetscSectionGetFieldConstraintDof(section, point, f, &fcdof);CHKERRQ(ierr);
4953   ierr = PetscSectionGetFieldOffset(section, point, f, &foff);CHKERRQ(ierr);
4954   a    = &array[foff];
4955   if (fcdof) {
4956     /* We just override fcdof and fcdofs with Ncc and comps */
4957     ierr = PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs);CHKERRQ(ierr);
4958     if (clperm) {
4959       if (perm) {
4960         if (comps) {
4961           for (b = 0; b < fdof; b++) {
4962             ncSet = fcSet = PETSC_FALSE;
4963             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
4964             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
4965             if (ncSet && fcSet) {fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));}
4966           }
4967         } else {
4968           for (b = 0; b < fdof; b++) {
4969             if ((cind < fcdof) && (b == fcdofs[cind])) {
4970               fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));
4971               ++cind;
4972             }
4973           }
4974         }
4975       } else {
4976         if (comps) {
4977           for (b = 0; b < fdof; b++) {
4978             ncSet = fcSet = PETSC_FALSE;
4979             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
4980             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
4981             if (ncSet && fcSet) {fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));}
4982           }
4983         } else {
4984           for (b = 0; b < fdof; b++) {
4985             if ((cind < fcdof) && (b == fcdofs[cind])) {
4986               fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));
4987               ++cind;
4988             }
4989           }
4990         }
4991       }
4992     } else {
4993       if (perm) {
4994         if (comps) {
4995           for (b = 0; b < fdof; b++) {
4996             ncSet = fcSet = PETSC_FALSE;
4997             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
4998             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
4999             if (ncSet && fcSet) {fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));}
5000           }
5001         } else {
5002           for (b = 0; b < fdof; b++) {
5003             if ((cind < fcdof) && (b == fcdofs[cind])) {
5004               fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));
5005               ++cind;
5006             }
5007           }
5008         }
5009       } else {
5010         if (comps) {
5011           for (b = 0; b < fdof; b++) {
5012             ncSet = fcSet = PETSC_FALSE;
5013             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
5014             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
5015             if (ncSet && fcSet) {fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));}
5016           }
5017         } else {
5018           for (b = 0; b < fdof; b++) {
5019             if ((cind < fcdof) && (b == fcdofs[cind])) {
5020               fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));
5021               ++cind;
5022             }
5023           }
5024         }
5025       }
5026     }
5027   }
5028   *offset += fdof;
5029   PetscFunctionReturn(0);
5030 }
5031 
5032 PETSC_STATIC_INLINE PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
5033 {
5034   PetscScalar    *array;
5035   const PetscInt *cone, *coneO;
5036   PetscInt        pStart, pEnd, p, numPoints, off, dof;
5037   PetscErrorCode  ierr;
5038 
5039   PetscFunctionBeginHot;
5040   ierr = PetscSectionGetChart(section, &pStart, &pEnd);CHKERRQ(ierr);
5041   ierr = DMPlexGetConeSize(dm, point, &numPoints);CHKERRQ(ierr);
5042   ierr = DMPlexGetCone(dm, point, &cone);CHKERRQ(ierr);
5043   ierr = DMPlexGetConeOrientation(dm, point, &coneO);CHKERRQ(ierr);
5044   ierr = VecGetArray(v, &array);CHKERRQ(ierr);
5045   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
5046     const PetscInt cp = !p ? point : cone[p-1];
5047     const PetscInt o  = !p ? 0     : coneO[p-1];
5048 
5049     if ((cp < pStart) || (cp >= pEnd)) {dof = 0; continue;}
5050     ierr = PetscSectionGetDof(section, cp, &dof);CHKERRQ(ierr);
5051     /* ADD_VALUES */
5052     {
5053       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
5054       PetscScalar    *a;
5055       PetscInt        cdof, coff, cind = 0, k;
5056 
5057       ierr = PetscSectionGetConstraintDof(section, cp, &cdof);CHKERRQ(ierr);
5058       ierr = PetscSectionGetOffset(section, cp, &coff);CHKERRQ(ierr);
5059       a    = &array[coff];
5060       if (!cdof) {
5061         if (o >= 0) {
5062           for (k = 0; k < dof; ++k) {
5063             a[k] += values[off+k];
5064           }
5065         } else {
5066           for (k = 0; k < dof; ++k) {
5067             a[k] += values[off+dof-k-1];
5068           }
5069         }
5070       } else {
5071         ierr = PetscSectionGetConstraintIndices(section, cp, &cdofs);CHKERRQ(ierr);
5072         if (o >= 0) {
5073           for (k = 0; k < dof; ++k) {
5074             if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5075             a[k] += values[off+k];
5076           }
5077         } else {
5078           for (k = 0; k < dof; ++k) {
5079             if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5080             a[k] += values[off+dof-k-1];
5081           }
5082         }
5083       }
5084     }
5085   }
5086   ierr = VecRestoreArray(v, &array);CHKERRQ(ierr);
5087   PetscFunctionReturn(0);
5088 }
5089 
5090 /*@C
5091   DMPlexVecSetClosure - Set an array of the values on the closure of 'point'
5092 
5093   Not collective
5094 
5095   Input Parameters:
5096 + dm - The DM
5097 . section - The section describing the layout in v, or NULL to use the default section
5098 . v - The local vector
5099 . point - The point in the DM
5100 . values - The array of values
5101 - mode - The insert mode. One of INSERT_ALL_VALUES, ADD_ALL_VALUES, INSERT_VALUES, ADD_VALUES, INSERT_BC_VALUES, and ADD_BC_VALUES,
5102          where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions.
5103 
5104   Fortran Notes:
5105   This routine is only available in Fortran 90, and you must include petsc.h90 in your code.
5106 
5107   Level: intermediate
5108 
5109 .seealso DMPlexVecGetClosure(), DMPlexMatSetClosure()
5110 @*/
5111 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
5112 {
5113   PetscSection    clSection;
5114   IS              clPoints;
5115   PetscScalar    *array;
5116   PetscInt       *points = NULL;
5117   const PetscInt *clp, *clperm;
5118   PetscInt        depth, numFields, numPoints, p;
5119   PetscErrorCode  ierr;
5120 
5121   PetscFunctionBeginHot;
5122   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5123   if (!section) {ierr = DMGetLocalSection(dm, &section);CHKERRQ(ierr);}
5124   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5125   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5126   ierr = DMPlexGetDepth(dm, &depth);CHKERRQ(ierr);
5127   ierr = PetscSectionGetNumFields(section, &numFields);CHKERRQ(ierr);
5128   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
5129     ierr = DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode);CHKERRQ(ierr);
5130     PetscFunctionReturn(0);
5131   }
5132   /* Get points */
5133   ierr = PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, NULL, &clperm);CHKERRQ(ierr);
5134   ierr = DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp);CHKERRQ(ierr);
5135   /* Get array */
5136   ierr = VecGetArray(v, &array);CHKERRQ(ierr);
5137   /* Get values */
5138   if (numFields > 0) {
5139     PetscInt offset = 0, f;
5140     for (f = 0; f < numFields; ++f) {
5141       const PetscInt    **perms = NULL;
5142       const PetscScalar **flips = NULL;
5143 
5144       ierr = PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips);CHKERRQ(ierr);
5145       switch (mode) {
5146       case INSERT_VALUES:
5147         for (p = 0; p < numPoints; p++) {
5148           const PetscInt    point = points[2*p];
5149           const PetscInt    *perm = perms ? perms[p] : NULL;
5150           const PetscScalar *flip = flips ? flips[p] : NULL;
5151           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array);
5152         } break;
5153       case INSERT_ALL_VALUES:
5154         for (p = 0; p < numPoints; p++) {
5155           const PetscInt    point = points[2*p];
5156           const PetscInt    *perm = perms ? perms[p] : NULL;
5157           const PetscScalar *flip = flips ? flips[p] : NULL;
5158           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array);
5159         } break;
5160       case INSERT_BC_VALUES:
5161         for (p = 0; p < numPoints; p++) {
5162           const PetscInt    point = points[2*p];
5163           const PetscInt    *perm = perms ? perms[p] : NULL;
5164           const PetscScalar *flip = flips ? flips[p] : NULL;
5165           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array);
5166         } break;
5167       case ADD_VALUES:
5168         for (p = 0; p < numPoints; p++) {
5169           const PetscInt    point = points[2*p];
5170           const PetscInt    *perm = perms ? perms[p] : NULL;
5171           const PetscScalar *flip = flips ? flips[p] : NULL;
5172           updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array);
5173         } break;
5174       case ADD_ALL_VALUES:
5175         for (p = 0; p < numPoints; p++) {
5176           const PetscInt    point = points[2*p];
5177           const PetscInt    *perm = perms ? perms[p] : NULL;
5178           const PetscScalar *flip = flips ? flips[p] : NULL;
5179           updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array);
5180         } break;
5181       case ADD_BC_VALUES:
5182         for (p = 0; p < numPoints; p++) {
5183           const PetscInt    point = points[2*p];
5184           const PetscInt    *perm = perms ? perms[p] : NULL;
5185           const PetscScalar *flip = flips ? flips[p] : NULL;
5186           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array);
5187         } break;
5188       default:
5189         SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
5190       }
5191       ierr = PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips);CHKERRQ(ierr);
5192     }
5193   } else {
5194     PetscInt dof, off;
5195     const PetscInt    **perms = NULL;
5196     const PetscScalar **flips = NULL;
5197 
5198     ierr = PetscSectionGetPointSyms(section,numPoints,points,&perms,&flips);CHKERRQ(ierr);
5199     switch (mode) {
5200     case INSERT_VALUES:
5201       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
5202         const PetscInt    point = points[2*p];
5203         const PetscInt    *perm = perms ? perms[p] : NULL;
5204         const PetscScalar *flip = flips ? flips[p] : NULL;
5205         ierr = PetscSectionGetDof(section, point, &dof);CHKERRQ(ierr);
5206         updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array);
5207       } break;
5208     case INSERT_ALL_VALUES:
5209       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
5210         const PetscInt    point = points[2*p];
5211         const PetscInt    *perm = perms ? perms[p] : NULL;
5212         const PetscScalar *flip = flips ? flips[p] : NULL;
5213         ierr = PetscSectionGetDof(section, point, &dof);CHKERRQ(ierr);
5214         updatePoint_private(section, point, dof, insert, PETSC_TRUE,  perm, flip, clperm, values, off, array);
5215       } break;
5216     case INSERT_BC_VALUES:
5217       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
5218         const PetscInt    point = points[2*p];
5219         const PetscInt    *perm = perms ? perms[p] : NULL;
5220         const PetscScalar *flip = flips ? flips[p] : NULL;
5221         ierr = PetscSectionGetDof(section, point, &dof);CHKERRQ(ierr);
5222         updatePointBC_private(section, point, dof, insert,  perm, flip, clperm, values, off, array);
5223       } break;
5224     case ADD_VALUES:
5225       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
5226         const PetscInt    point = points[2*p];
5227         const PetscInt    *perm = perms ? perms[p] : NULL;
5228         const PetscScalar *flip = flips ? flips[p] : NULL;
5229         ierr = PetscSectionGetDof(section, point, &dof);CHKERRQ(ierr);
5230         updatePoint_private(section, point, dof, add,    PETSC_FALSE, perm, flip, clperm, values, off, array);
5231       } break;
5232     case ADD_ALL_VALUES:
5233       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
5234         const PetscInt    point = points[2*p];
5235         const PetscInt    *perm = perms ? perms[p] : NULL;
5236         const PetscScalar *flip = flips ? flips[p] : NULL;
5237         ierr = PetscSectionGetDof(section, point, &dof);CHKERRQ(ierr);
5238         updatePoint_private(section, point, dof, add,    PETSC_TRUE,  perm, flip, clperm, values, off, array);
5239       } break;
5240     case ADD_BC_VALUES:
5241       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
5242         const PetscInt    point = points[2*p];
5243         const PetscInt    *perm = perms ? perms[p] : NULL;
5244         const PetscScalar *flip = flips ? flips[p] : NULL;
5245         ierr = PetscSectionGetDof(section, point, &dof);CHKERRQ(ierr);
5246         updatePointBC_private(section, point, dof, add,  perm, flip, clperm, values, off, array);
5247       } break;
5248     default:
5249       SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
5250     }
5251     ierr = PetscSectionRestorePointSyms(section,numPoints,points,&perms,&flips);CHKERRQ(ierr);
5252   }
5253   /* Cleanup points */
5254   ierr = DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp);CHKERRQ(ierr);
5255   /* Cleanup array */
5256   ierr = VecRestoreArray(v, &array);CHKERRQ(ierr);
5257   PetscFunctionReturn(0);
5258 }
5259 
5260 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
5261 PetscErrorCode DMPlexVecSetFieldClosure_Internal(DM dm, PetscSection section, Vec v, PetscBool fieldActive[], PetscInt point, PetscInt Ncc, const PetscInt comps[], const PetscScalar values[], InsertMode mode)
5262 {
5263   PetscSection      clSection;
5264   IS                clPoints;
5265   PetscScalar       *array;
5266   PetscInt          *points = NULL;
5267   const PetscInt    *clp;
5268   PetscInt          numFields, numPoints, p;
5269   PetscInt          offset = 0, f;
5270   PetscErrorCode    ierr;
5271 
5272   PetscFunctionBeginHot;
5273   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5274   if (!section) {ierr = DMGetLocalSection(dm, &section);CHKERRQ(ierr);}
5275   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5276   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5277   ierr = PetscSectionGetNumFields(section, &numFields);CHKERRQ(ierr);
5278   /* Get points */
5279   ierr = DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp);CHKERRQ(ierr);
5280   /* Get array */
5281   ierr = VecGetArray(v, &array);CHKERRQ(ierr);
5282   /* Get values */
5283   for (f = 0; f < numFields; ++f) {
5284     const PetscInt    **perms = NULL;
5285     const PetscScalar **flips = NULL;
5286 
5287     if (!fieldActive[f]) {
5288       for (p = 0; p < numPoints*2; p += 2) {
5289         PetscInt fdof;
5290         ierr = PetscSectionGetFieldDof(section, points[p], f, &fdof);CHKERRQ(ierr);
5291         offset += fdof;
5292       }
5293       continue;
5294     }
5295     ierr = PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips);CHKERRQ(ierr);
5296     switch (mode) {
5297     case INSERT_VALUES:
5298       for (p = 0; p < numPoints; p++) {
5299         const PetscInt    point = points[2*p];
5300         const PetscInt    *perm = perms ? perms[p] : NULL;
5301         const PetscScalar *flip = flips ? flips[p] : NULL;
5302         updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array);
5303       } break;
5304     case INSERT_ALL_VALUES:
5305       for (p = 0; p < numPoints; p++) {
5306         const PetscInt    point = points[2*p];
5307         const PetscInt    *perm = perms ? perms[p] : NULL;
5308         const PetscScalar *flip = flips ? flips[p] : NULL;
5309         updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array);
5310         } break;
5311     case INSERT_BC_VALUES:
5312       for (p = 0; p < numPoints; p++) {
5313         const PetscInt    point = points[2*p];
5314         const PetscInt    *perm = perms ? perms[p] : NULL;
5315         const PetscScalar *flip = flips ? flips[p] : NULL;
5316         updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array);
5317       } break;
5318     case ADD_VALUES:
5319       for (p = 0; p < numPoints; p++) {
5320         const PetscInt    point = points[2*p];
5321         const PetscInt    *perm = perms ? perms[p] : NULL;
5322         const PetscScalar *flip = flips ? flips[p] : NULL;
5323         updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array);
5324       } break;
5325     case ADD_ALL_VALUES:
5326       for (p = 0; p < numPoints; p++) {
5327         const PetscInt    point = points[2*p];
5328         const PetscInt    *perm = perms ? perms[p] : NULL;
5329         const PetscScalar *flip = flips ? flips[p] : NULL;
5330         updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array);
5331       } break;
5332     default:
5333       SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
5334     }
5335     ierr = PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips);CHKERRQ(ierr);
5336   }
5337   /* Cleanup points */
5338   ierr = DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp);CHKERRQ(ierr);
5339   /* Cleanup array */
5340   ierr = VecRestoreArray(v, &array);CHKERRQ(ierr);
5341   PetscFunctionReturn(0);
5342 }
5343 
5344 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
5345 {
5346   PetscMPIInt    rank;
5347   PetscInt       i, j;
5348   PetscErrorCode ierr;
5349 
5350   PetscFunctionBegin;
5351   ierr = MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank);CHKERRQ(ierr);
5352   ierr = PetscViewerASCIIPrintf(viewer, "[%d]mat for point %D\n", rank, point);CHKERRQ(ierr);
5353   for (i = 0; i < numRIndices; i++) {ierr = PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%D] = %D\n", rank, i, rindices[i]);CHKERRQ(ierr);}
5354   for (i = 0; i < numCIndices; i++) {ierr = PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%D] = %D\n", rank, i, cindices[i]);CHKERRQ(ierr);}
5355   numCIndices = numCIndices ? numCIndices : numRIndices;
5356   for (i = 0; i < numRIndices; i++) {
5357     ierr = PetscViewerASCIIPrintf(viewer, "[%d]", rank);CHKERRQ(ierr);
5358     for (j = 0; j < numCIndices; j++) {
5359 #if defined(PETSC_USE_COMPLEX)
5360       ierr = PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i*numCIndices+j]), (double)PetscImaginaryPart(values[i*numCIndices+j]));CHKERRQ(ierr);
5361 #else
5362       ierr = PetscViewerASCIIPrintf(viewer, " %g", (double)values[i*numCIndices+j]);CHKERRQ(ierr);
5363 #endif
5364     }
5365     ierr = PetscViewerASCIIPrintf(viewer, "\n");CHKERRQ(ierr);
5366   }
5367   PetscFunctionReturn(0);
5368 }
5369 
5370 /*
5371   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
5372 
5373   Input Parameters:
5374 + section - The section for this data layout
5375 . islocal - Is the section (and thus indices being requested) local or global?
5376 . point   - The point contributing dofs with these indices
5377 . off     - The global offset of this point
5378 . loff    - The local offset of each field
5379 . setBC   - The flag determining whether to include indices of bounsary values
5380 . perm    - A permutation of the dofs on this point, or NULL
5381 - indperm - A permutation of the entire indices array, or NULL
5382 
5383   Output Parameter:
5384 . indices - Indices for dofs on this point
5385 
5386   Level: developer
5387 
5388   Note: The indices could be local or global, depending on the value of 'off'.
5389 */
5390 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal,PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
5391 {
5392   PetscInt        dof;   /* The number of unknowns on this point */
5393   PetscInt        cdof;  /* The number of constraints on this point */
5394   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
5395   PetscInt        cind = 0, k;
5396   PetscErrorCode  ierr;
5397 
5398   PetscFunctionBegin;
5399   if (!islocal && setBC) SETERRQ(PetscObjectComm((PetscObject)section),PETSC_ERR_ARG_INCOMP,"setBC incompatible with global indices; use a local section or disable setBC");
5400   ierr = PetscSectionGetDof(section, point, &dof);CHKERRQ(ierr);
5401   ierr = PetscSectionGetConstraintDof(section, point, &cdof);CHKERRQ(ierr);
5402   if (!cdof || setBC) {
5403     for (k = 0; k < dof; ++k) {
5404       const PetscInt preind = perm ? *loff+perm[k] : *loff+k;
5405       const PetscInt ind    = indperm ? indperm[preind] : preind;
5406 
5407       indices[ind] = off + k;
5408     }
5409   } else {
5410     ierr = PetscSectionGetConstraintIndices(section, point, &cdofs);CHKERRQ(ierr);
5411     for (k = 0; k < dof; ++k) {
5412       const PetscInt preind = perm ? *loff+perm[k] : *loff+k;
5413       const PetscInt ind    = indperm ? indperm[preind] : preind;
5414 
5415       if ((cind < cdof) && (k == cdofs[cind])) {
5416         /* Insert check for returning constrained indices */
5417         indices[ind] = -(off+k+1);
5418         ++cind;
5419       } else {
5420         indices[ind] = off + k - (islocal ? 0 : cind);
5421       }
5422     }
5423   }
5424   *loff += dof;
5425   PetscFunctionReturn(0);
5426 }
5427 
5428 /*
5429  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
5430 
5431  Input Parameters:
5432 + section - a section (global or local)
5433 - islocal - PETSC_TRUE if requesting local indices (i.e., section is local); PETSC_FALSE for global
5434 . point - point within section
5435 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
5436 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
5437 . setBC - identify constrained (boundary condition) points via involution.
5438 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
5439 . permsoff - offset
5440 - indperm - index permutation
5441 
5442  Output Parameter:
5443 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
5444 . indices - array to hold indices (as defined by section) of each dof associated with point
5445 
5446  Notes:
5447  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
5448  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
5449  in the local vector.
5450 
5451  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
5452  significant).  It is invalid to call with a global section and setBC=true.
5453 
5454  Developer Note:
5455  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
5456  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
5457  offset could be obtained from the section instead of passing it explicitly as we do now.
5458 
5459  Example:
5460  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
5461  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
5462  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
5463  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.
5464 
5465  Level: developer
5466 */
5467 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[])
5468 {
5469   PetscInt       numFields, foff, f;
5470   PetscErrorCode ierr;
5471 
5472   PetscFunctionBegin;
5473   if (!islocal && setBC) SETERRQ(PetscObjectComm((PetscObject)section),PETSC_ERR_ARG_INCOMP,"setBC incompatible with global indices; use a local section or disable setBC");
5474   ierr = PetscSectionGetNumFields(section, &numFields);CHKERRQ(ierr);
5475   for (f = 0, foff = 0; f < numFields; ++f) {
5476     PetscInt        fdof, cfdof;
5477     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
5478     PetscInt        cind = 0, b;
5479     const PetscInt  *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
5480 
5481     ierr = PetscSectionGetFieldDof(section, point, f, &fdof);CHKERRQ(ierr);
5482     ierr = PetscSectionGetFieldConstraintDof(section, point, f, &cfdof);CHKERRQ(ierr);
5483     if (!cfdof || setBC) {
5484       for (b = 0; b < fdof; ++b) {
5485         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
5486         const PetscInt ind    = indperm ? indperm[preind] : preind;
5487 
5488         indices[ind] = off+foff+b;
5489       }
5490     } else {
5491       ierr = PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs);CHKERRQ(ierr);
5492       for (b = 0; b < fdof; ++b) {
5493         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
5494         const PetscInt ind    = indperm ? indperm[preind] : preind;
5495 
5496         if ((cind < cfdof) && (b == fcdofs[cind])) {
5497           indices[ind] = -(off+foff+b+1);
5498           ++cind;
5499         } else {
5500           indices[ind] = off + foff + b - (islocal ? 0 : cind);
5501         }
5502       }
5503     }
5504     foff     += (setBC || islocal ? fdof : (fdof - cfdof));
5505     foffs[f] += fdof;
5506   }
5507   PetscFunctionReturn(0);
5508 }
5509 
5510 /*
5511   This version believes the globalSection offsets for each field, rather than just the point offset
5512 
5513  . foffs - The offset into 'indices' for each field, since it is segregated by field
5514 
5515  Notes:
5516  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
5517  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
5518 */
5519 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
5520 {
5521   PetscInt       numFields, foff, f;
5522   PetscErrorCode ierr;
5523 
5524   PetscFunctionBegin;
5525   ierr = PetscSectionGetNumFields(section, &numFields);CHKERRQ(ierr);
5526   for (f = 0; f < numFields; ++f) {
5527     PetscInt        fdof, cfdof;
5528     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
5529     PetscInt        cind = 0, b;
5530     const PetscInt  *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
5531 
5532     ierr = PetscSectionGetFieldDof(section, point, f, &fdof);CHKERRQ(ierr);
5533     ierr = PetscSectionGetFieldConstraintDof(section, point, f, &cfdof);CHKERRQ(ierr);
5534     ierr = PetscSectionGetFieldOffset(globalSection, point, f, &foff);CHKERRQ(ierr);
5535     if (!cfdof) {
5536       for (b = 0; b < fdof; ++b) {
5537         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
5538         const PetscInt ind    = indperm ? indperm[preind] : preind;
5539 
5540         indices[ind] = foff+b;
5541       }
5542     } else {
5543       ierr = PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs);CHKERRQ(ierr);
5544       for (b = 0; b < fdof; ++b) {
5545         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
5546         const PetscInt ind    = indperm ? indperm[preind] : preind;
5547 
5548         if ((cind < cfdof) && (b == fcdofs[cind])) {
5549           indices[ind] = -(foff+b+1);
5550           ++cind;
5551         } else {
5552           indices[ind] = foff+b-cind;
5553         }
5554       }
5555     }
5556     foffs[f] += fdof;
5557   }
5558   PetscFunctionReturn(0);
5559 }
5560 
5561 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)
5562 {
5563   Mat             cMat;
5564   PetscSection    aSec, cSec;
5565   IS              aIS;
5566   PetscInt        aStart = -1, aEnd = -1;
5567   const PetscInt  *anchors;
5568   PetscInt        numFields, f, p, q, newP = 0;
5569   PetscInt        newNumPoints = 0, newNumIndices = 0;
5570   PetscInt        *newPoints, *indices, *newIndices;
5571   PetscInt        maxAnchor, maxDof;
5572   PetscInt        newOffsets[32];
5573   PetscInt        *pointMatOffsets[32];
5574   PetscInt        *newPointOffsets[32];
5575   PetscScalar     *pointMat[32];
5576   PetscScalar     *newValues=NULL,*tmpValues;
5577   PetscBool       anyConstrained = PETSC_FALSE;
5578   PetscErrorCode  ierr;
5579 
5580   PetscFunctionBegin;
5581   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5582   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5583   ierr = PetscSectionGetNumFields(section, &numFields);CHKERRQ(ierr);
5584 
5585   ierr = DMPlexGetAnchors(dm,&aSec,&aIS);CHKERRQ(ierr);
5586   /* if there are point-to-point constraints */
5587   if (aSec) {
5588     ierr = PetscArrayzero(newOffsets, 32);CHKERRQ(ierr);
5589     ierr = ISGetIndices(aIS,&anchors);CHKERRQ(ierr);
5590     ierr = PetscSectionGetChart(aSec,&aStart,&aEnd);CHKERRQ(ierr);
5591     /* figure out how many points are going to be in the new element matrix
5592      * (we allow double counting, because it's all just going to be summed
5593      * into the global matrix anyway) */
5594     for (p = 0; p < 2*numPoints; p+=2) {
5595       PetscInt b    = points[p];
5596       PetscInt bDof = 0, bSecDof;
5597 
5598       ierr = PetscSectionGetDof(section,b,&bSecDof);CHKERRQ(ierr);
5599       if (!bSecDof) {
5600         continue;
5601       }
5602       if (b >= aStart && b < aEnd) {
5603         ierr = PetscSectionGetDof(aSec,b,&bDof);CHKERRQ(ierr);
5604       }
5605       if (bDof) {
5606         /* this point is constrained */
5607         /* it is going to be replaced by its anchors */
5608         PetscInt bOff, q;
5609 
5610         anyConstrained = PETSC_TRUE;
5611         newNumPoints  += bDof;
5612         ierr = PetscSectionGetOffset(aSec,b,&bOff);CHKERRQ(ierr);
5613         for (q = 0; q < bDof; q++) {
5614           PetscInt a = anchors[bOff + q];
5615           PetscInt aDof;
5616 
5617           ierr           = PetscSectionGetDof(section,a,&aDof);CHKERRQ(ierr);
5618           newNumIndices += aDof;
5619           for (f = 0; f < numFields; ++f) {
5620             PetscInt fDof;
5621 
5622             ierr             = PetscSectionGetFieldDof(section, a, f, &fDof);CHKERRQ(ierr);
5623             newOffsets[f+1] += fDof;
5624           }
5625         }
5626       }
5627       else {
5628         /* this point is not constrained */
5629         newNumPoints++;
5630         newNumIndices += bSecDof;
5631         for (f = 0; f < numFields; ++f) {
5632           PetscInt fDof;
5633 
5634           ierr = PetscSectionGetFieldDof(section, b, f, &fDof);CHKERRQ(ierr);
5635           newOffsets[f+1] += fDof;
5636         }
5637       }
5638     }
5639   }
5640   if (!anyConstrained) {
5641     if (outNumPoints)  *outNumPoints  = 0;
5642     if (outNumIndices) *outNumIndices = 0;
5643     if (outPoints)     *outPoints     = NULL;
5644     if (outValues)     *outValues     = NULL;
5645     if (aSec) {ierr = ISRestoreIndices(aIS,&anchors);CHKERRQ(ierr);}
5646     PetscFunctionReturn(0);
5647   }
5648 
5649   if (outNumPoints)  *outNumPoints  = newNumPoints;
5650   if (outNumIndices) *outNumIndices = newNumIndices;
5651 
5652   for (f = 0; f < numFields; ++f) newOffsets[f+1] += newOffsets[f];
5653 
5654   if (!outPoints && !outValues) {
5655     if (offsets) {
5656       for (f = 0; f <= numFields; f++) {
5657         offsets[f] = newOffsets[f];
5658       }
5659     }
5660     if (aSec) {ierr = ISRestoreIndices(aIS,&anchors);CHKERRQ(ierr);}
5661     PetscFunctionReturn(0);
5662   }
5663 
5664   if (numFields && newOffsets[numFields] != newNumIndices) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %D should be %D", newOffsets[numFields], newNumIndices);
5665 
5666   ierr = DMGetDefaultConstraints(dm, &cSec, &cMat);CHKERRQ(ierr);
5667 
5668   /* workspaces */
5669   if (numFields) {
5670     for (f = 0; f < numFields; f++) {
5671       ierr = DMGetWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[f]);CHKERRQ(ierr);
5672       ierr = DMGetWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[f]);CHKERRQ(ierr);
5673     }
5674   }
5675   else {
5676     ierr = DMGetWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[0]);CHKERRQ(ierr);
5677     ierr = DMGetWorkArray(dm,numPoints,MPIU_INT,&newPointOffsets[0]);CHKERRQ(ierr);
5678   }
5679 
5680   /* get workspaces for the point-to-point matrices */
5681   if (numFields) {
5682     PetscInt totalOffset, totalMatOffset;
5683 
5684     for (p = 0; p < numPoints; p++) {
5685       PetscInt b    = points[2*p];
5686       PetscInt bDof = 0, bSecDof;
5687 
5688       ierr = PetscSectionGetDof(section,b,&bSecDof);CHKERRQ(ierr);
5689       if (!bSecDof) {
5690         for (f = 0; f < numFields; f++) {
5691           newPointOffsets[f][p + 1] = 0;
5692           pointMatOffsets[f][p + 1] = 0;
5693         }
5694         continue;
5695       }
5696       if (b >= aStart && b < aEnd) {
5697         ierr = PetscSectionGetDof(aSec, b, &bDof);CHKERRQ(ierr);
5698       }
5699       if (bDof) {
5700         for (f = 0; f < numFields; f++) {
5701           PetscInt fDof, q, bOff, allFDof = 0;
5702 
5703           ierr = PetscSectionGetFieldDof(section, b, f, &fDof);CHKERRQ(ierr);
5704           ierr = PetscSectionGetOffset(aSec, b, &bOff);CHKERRQ(ierr);
5705           for (q = 0; q < bDof; q++) {
5706             PetscInt a = anchors[bOff + q];
5707             PetscInt aFDof;
5708 
5709             ierr     = PetscSectionGetFieldDof(section, a, f, &aFDof);CHKERRQ(ierr);
5710             allFDof += aFDof;
5711           }
5712           newPointOffsets[f][p+1] = allFDof;
5713           pointMatOffsets[f][p+1] = fDof * allFDof;
5714         }
5715       }
5716       else {
5717         for (f = 0; f < numFields; f++) {
5718           PetscInt fDof;
5719 
5720           ierr = PetscSectionGetFieldDof(section, b, f, &fDof);CHKERRQ(ierr);
5721           newPointOffsets[f][p+1] = fDof;
5722           pointMatOffsets[f][p+1] = 0;
5723         }
5724       }
5725     }
5726     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
5727       newPointOffsets[f][0] = totalOffset;
5728       pointMatOffsets[f][0] = totalMatOffset;
5729       for (p = 0; p < numPoints; p++) {
5730         newPointOffsets[f][p+1] += newPointOffsets[f][p];
5731         pointMatOffsets[f][p+1] += pointMatOffsets[f][p];
5732       }
5733       totalOffset    = newPointOffsets[f][numPoints];
5734       totalMatOffset = pointMatOffsets[f][numPoints];
5735       ierr = DMGetWorkArray(dm,pointMatOffsets[f][numPoints],MPIU_SCALAR,&pointMat[f]);CHKERRQ(ierr);
5736     }
5737   }
5738   else {
5739     for (p = 0; p < numPoints; p++) {
5740       PetscInt b    = points[2*p];
5741       PetscInt bDof = 0, bSecDof;
5742 
5743       ierr = PetscSectionGetDof(section,b,&bSecDof);CHKERRQ(ierr);
5744       if (!bSecDof) {
5745         newPointOffsets[0][p + 1] = 0;
5746         pointMatOffsets[0][p + 1] = 0;
5747         continue;
5748       }
5749       if (b >= aStart && b < aEnd) {
5750         ierr = PetscSectionGetDof(aSec, b, &bDof);CHKERRQ(ierr);
5751       }
5752       if (bDof) {
5753         PetscInt bOff, q, allDof = 0;
5754 
5755         ierr = PetscSectionGetOffset(aSec, b, &bOff);CHKERRQ(ierr);
5756         for (q = 0; q < bDof; q++) {
5757           PetscInt a = anchors[bOff + q], aDof;
5758 
5759           ierr    = PetscSectionGetDof(section, a, &aDof);CHKERRQ(ierr);
5760           allDof += aDof;
5761         }
5762         newPointOffsets[0][p+1] = allDof;
5763         pointMatOffsets[0][p+1] = bSecDof * allDof;
5764       }
5765       else {
5766         newPointOffsets[0][p+1] = bSecDof;
5767         pointMatOffsets[0][p+1] = 0;
5768       }
5769     }
5770     newPointOffsets[0][0] = 0;
5771     pointMatOffsets[0][0] = 0;
5772     for (p = 0; p < numPoints; p++) {
5773       newPointOffsets[0][p+1] += newPointOffsets[0][p];
5774       pointMatOffsets[0][p+1] += pointMatOffsets[0][p];
5775     }
5776     ierr = DMGetWorkArray(dm,pointMatOffsets[0][numPoints],MPIU_SCALAR,&pointMat[0]);CHKERRQ(ierr);
5777   }
5778 
5779   /* output arrays */
5780   ierr = DMGetWorkArray(dm,2*newNumPoints,MPIU_INT,&newPoints);CHKERRQ(ierr);
5781 
5782   /* get the point-to-point matrices; construct newPoints */
5783   ierr = PetscSectionGetMaxDof(aSec, &maxAnchor);CHKERRQ(ierr);
5784   ierr = PetscSectionGetMaxDof(section, &maxDof);CHKERRQ(ierr);
5785   ierr = DMGetWorkArray(dm,maxDof,MPIU_INT,&indices);CHKERRQ(ierr);
5786   ierr = DMGetWorkArray(dm,maxAnchor*maxDof,MPIU_INT,&newIndices);CHKERRQ(ierr);
5787   if (numFields) {
5788     for (p = 0, newP = 0; p < numPoints; p++) {
5789       PetscInt b    = points[2*p];
5790       PetscInt o    = points[2*p+1];
5791       PetscInt bDof = 0, bSecDof;
5792 
5793       ierr = PetscSectionGetDof(section, b, &bSecDof);CHKERRQ(ierr);
5794       if (!bSecDof) {
5795         continue;
5796       }
5797       if (b >= aStart && b < aEnd) {
5798         ierr = PetscSectionGetDof(aSec, b, &bDof);CHKERRQ(ierr);
5799       }
5800       if (bDof) {
5801         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
5802 
5803         fStart[0] = 0;
5804         fEnd[0]   = 0;
5805         for (f = 0; f < numFields; f++) {
5806           PetscInt fDof;
5807 
5808           ierr        = PetscSectionGetFieldDof(cSec, b, f, &fDof);CHKERRQ(ierr);
5809           fStart[f+1] = fStart[f] + fDof;
5810           fEnd[f+1]   = fStart[f+1];
5811         }
5812         ierr = PetscSectionGetOffset(cSec, b, &bOff);CHKERRQ(ierr);
5813         ierr = DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices);CHKERRQ(ierr);
5814 
5815         fAnchorStart[0] = 0;
5816         fAnchorEnd[0]   = 0;
5817         for (f = 0; f < numFields; f++) {
5818           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
5819 
5820           fAnchorStart[f+1] = fAnchorStart[f] + fDof;
5821           fAnchorEnd[f+1]   = fAnchorStart[f + 1];
5822         }
5823         ierr = PetscSectionGetOffset(aSec, b, &bOff);CHKERRQ(ierr);
5824         for (q = 0; q < bDof; q++) {
5825           PetscInt a = anchors[bOff + q], aOff;
5826 
5827           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
5828           newPoints[2*(newP + q)]     = a;
5829           newPoints[2*(newP + q) + 1] = 0;
5830           ierr = PetscSectionGetOffset(section, a, &aOff);CHKERRQ(ierr);
5831           ierr = DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices);CHKERRQ(ierr);
5832         }
5833         newP += bDof;
5834 
5835         if (outValues) {
5836           /* get the point-to-point submatrix */
5837           for (f = 0; f < numFields; f++) {
5838             ierr = MatGetValues(cMat,fEnd[f]-fStart[f],indices + fStart[f],fAnchorEnd[f] - fAnchorStart[f],newIndices + fAnchorStart[f],pointMat[f] + pointMatOffsets[f][p]);CHKERRQ(ierr);
5839           }
5840         }
5841       }
5842       else {
5843         newPoints[2 * newP]     = b;
5844         newPoints[2 * newP + 1] = o;
5845         newP++;
5846       }
5847     }
5848   } else {
5849     for (p = 0; p < numPoints; p++) {
5850       PetscInt b    = points[2*p];
5851       PetscInt o    = points[2*p+1];
5852       PetscInt bDof = 0, bSecDof;
5853 
5854       ierr = PetscSectionGetDof(section, b, &bSecDof);CHKERRQ(ierr);
5855       if (!bSecDof) {
5856         continue;
5857       }
5858       if (b >= aStart && b < aEnd) {
5859         ierr = PetscSectionGetDof(aSec, b, &bDof);CHKERRQ(ierr);
5860       }
5861       if (bDof) {
5862         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
5863 
5864         ierr = PetscSectionGetOffset(cSec, b, &bOff);CHKERRQ(ierr);
5865         ierr = DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices);CHKERRQ(ierr);
5866 
5867         ierr = PetscSectionGetOffset (aSec, b, &bOff);CHKERRQ(ierr);
5868         for (q = 0; q < bDof; q++) {
5869           PetscInt a = anchors[bOff + q], aOff;
5870 
5871           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
5872 
5873           newPoints[2*(newP + q)]     = a;
5874           newPoints[2*(newP + q) + 1] = 0;
5875           ierr = PetscSectionGetOffset(section, a, &aOff);CHKERRQ(ierr);
5876           ierr = DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices);CHKERRQ(ierr);
5877         }
5878         newP += bDof;
5879 
5880         /* get the point-to-point submatrix */
5881         if (outValues) {
5882           ierr = MatGetValues(cMat,bEnd,indices,bAnchorEnd,newIndices,pointMat[0] + pointMatOffsets[0][p]);CHKERRQ(ierr);
5883         }
5884       }
5885       else {
5886         newPoints[2 * newP]     = b;
5887         newPoints[2 * newP + 1] = o;
5888         newP++;
5889       }
5890     }
5891   }
5892 
5893   if (outValues) {
5894     ierr = DMGetWorkArray(dm,newNumIndices*numIndices,MPIU_SCALAR,&tmpValues);CHKERRQ(ierr);
5895     ierr = PetscArrayzero(tmpValues,newNumIndices*numIndices);CHKERRQ(ierr);
5896     /* multiply constraints on the right */
5897     if (numFields) {
5898       for (f = 0; f < numFields; f++) {
5899         PetscInt oldOff = offsets[f];
5900 
5901         for (p = 0; p < numPoints; p++) {
5902           PetscInt cStart = newPointOffsets[f][p];
5903           PetscInt b      = points[2 * p];
5904           PetscInt c, r, k;
5905           PetscInt dof;
5906 
5907           ierr = PetscSectionGetFieldDof(section,b,f,&dof);CHKERRQ(ierr);
5908           if (!dof) {
5909             continue;
5910           }
5911           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
5912             PetscInt nCols         = newPointOffsets[f][p+1]-cStart;
5913             const PetscScalar *mat = pointMat[f] + pointMatOffsets[f][p];
5914 
5915             for (r = 0; r < numIndices; r++) {
5916               for (c = 0; c < nCols; c++) {
5917                 for (k = 0; k < dof; k++) {
5918                   tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
5919                 }
5920               }
5921             }
5922           }
5923           else {
5924             /* copy this column as is */
5925             for (r = 0; r < numIndices; r++) {
5926               for (c = 0; c < dof; c++) {
5927                 tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
5928               }
5929             }
5930           }
5931           oldOff += dof;
5932         }
5933       }
5934     }
5935     else {
5936       PetscInt oldOff = 0;
5937       for (p = 0; p < numPoints; p++) {
5938         PetscInt cStart = newPointOffsets[0][p];
5939         PetscInt b      = points[2 * p];
5940         PetscInt c, r, k;
5941         PetscInt dof;
5942 
5943         ierr = PetscSectionGetDof(section,b,&dof);CHKERRQ(ierr);
5944         if (!dof) {
5945           continue;
5946         }
5947         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
5948           PetscInt nCols         = newPointOffsets[0][p+1]-cStart;
5949           const PetscScalar *mat = pointMat[0] + pointMatOffsets[0][p];
5950 
5951           for (r = 0; r < numIndices; r++) {
5952             for (c = 0; c < nCols; c++) {
5953               for (k = 0; k < dof; k++) {
5954                 tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
5955               }
5956             }
5957           }
5958         }
5959         else {
5960           /* copy this column as is */
5961           for (r = 0; r < numIndices; r++) {
5962             for (c = 0; c < dof; c++) {
5963               tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
5964             }
5965           }
5966         }
5967         oldOff += dof;
5968       }
5969     }
5970 
5971     if (multiplyLeft) {
5972       ierr = DMGetWorkArray(dm,newNumIndices*newNumIndices,MPIU_SCALAR,&newValues);CHKERRQ(ierr);
5973       ierr = PetscArrayzero(newValues,newNumIndices*newNumIndices);CHKERRQ(ierr);
5974       /* multiply constraints transpose on the left */
5975       if (numFields) {
5976         for (f = 0; f < numFields; f++) {
5977           PetscInt oldOff = offsets[f];
5978 
5979           for (p = 0; p < numPoints; p++) {
5980             PetscInt rStart = newPointOffsets[f][p];
5981             PetscInt b      = points[2 * p];
5982             PetscInt c, r, k;
5983             PetscInt dof;
5984 
5985             ierr = PetscSectionGetFieldDof(section,b,f,&dof);CHKERRQ(ierr);
5986             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
5987               PetscInt nRows                        = newPointOffsets[f][p+1]-rStart;
5988               const PetscScalar *PETSC_RESTRICT mat = pointMat[f] + pointMatOffsets[f][p];
5989 
5990               for (r = 0; r < nRows; r++) {
5991                 for (c = 0; c < newNumIndices; c++) {
5992                   for (k = 0; k < dof; k++) {
5993                     newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
5994                   }
5995                 }
5996               }
5997             }
5998             else {
5999               /* copy this row as is */
6000               for (r = 0; r < dof; r++) {
6001                 for (c = 0; c < newNumIndices; c++) {
6002                   newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
6003                 }
6004               }
6005             }
6006             oldOff += dof;
6007           }
6008         }
6009       }
6010       else {
6011         PetscInt oldOff = 0;
6012 
6013         for (p = 0; p < numPoints; p++) {
6014           PetscInt rStart = newPointOffsets[0][p];
6015           PetscInt b      = points[2 * p];
6016           PetscInt c, r, k;
6017           PetscInt dof;
6018 
6019           ierr = PetscSectionGetDof(section,b,&dof);CHKERRQ(ierr);
6020           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
6021             PetscInt nRows                        = newPointOffsets[0][p+1]-rStart;
6022             const PetscScalar *PETSC_RESTRICT mat = pointMat[0] + pointMatOffsets[0][p];
6023 
6024             for (r = 0; r < nRows; r++) {
6025               for (c = 0; c < newNumIndices; c++) {
6026                 for (k = 0; k < dof; k++) {
6027                   newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
6028                 }
6029               }
6030             }
6031           }
6032           else {
6033             /* copy this row as is */
6034             for (r = 0; r < dof; r++) {
6035               for (c = 0; c < newNumIndices; c++) {
6036                 newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
6037               }
6038             }
6039           }
6040           oldOff += dof;
6041         }
6042       }
6043 
6044       ierr = DMRestoreWorkArray(dm,newNumIndices*numIndices,MPIU_SCALAR,&tmpValues);CHKERRQ(ierr);
6045     }
6046     else {
6047       newValues = tmpValues;
6048     }
6049   }
6050 
6051   /* clean up */
6052   ierr = DMRestoreWorkArray(dm,maxDof,MPIU_INT,&indices);CHKERRQ(ierr);
6053   ierr = DMRestoreWorkArray(dm,maxAnchor*maxDof,MPIU_INT,&newIndices);CHKERRQ(ierr);
6054 
6055   if (numFields) {
6056     for (f = 0; f < numFields; f++) {
6057       ierr = DMRestoreWorkArray(dm,pointMatOffsets[f][numPoints],MPIU_SCALAR,&pointMat[f]);CHKERRQ(ierr);
6058       ierr = DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[f]);CHKERRQ(ierr);
6059       ierr = DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[f]);CHKERRQ(ierr);
6060     }
6061   }
6062   else {
6063     ierr = DMRestoreWorkArray(dm,pointMatOffsets[0][numPoints],MPIU_SCALAR,&pointMat[0]);CHKERRQ(ierr);
6064     ierr = DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[0]);CHKERRQ(ierr);
6065     ierr = DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[0]);CHKERRQ(ierr);
6066   }
6067   ierr = ISRestoreIndices(aIS,&anchors);CHKERRQ(ierr);
6068 
6069   /* output */
6070   if (outPoints) {
6071     *outPoints = newPoints;
6072   }
6073   else {
6074     ierr = DMRestoreWorkArray(dm,2*newNumPoints,MPIU_INT,&newPoints);CHKERRQ(ierr);
6075   }
6076   if (outValues) {
6077     *outValues = newValues;
6078   }
6079   for (f = 0; f <= numFields; f++) {
6080     offsets[f] = newOffsets[f];
6081   }
6082   PetscFunctionReturn(0);
6083 }
6084 
6085 /*@C
6086   DMPlexGetClosureIndices - Get the global indices for all local points in the closure of the given point
6087 
6088   Not collective
6089 
6090   Input Parameters:
6091 + dm - The DM
6092 . section - The section describing the points (a local section)
6093 . idxSection - The section on which to obtain indices (may be local or global)
6094 - point - The mesh point
6095 
6096   Output parameters:
6097 + numIndices - The number of indices
6098 . indices - The indices
6099 - outOffsets - Field offset if not NULL
6100 
6101   Notes:
6102   Must call DMPlexRestoreClosureIndices() to free allocated memory
6103 
6104   If idxSection is global, any constrained dofs (see DMAddBoundary(), for example) will get negative indices.  The value
6105   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
6106   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
6107   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
6108   indices (with the above semantics) are implied.
6109 
6110   Level: advanced
6111 
6112 .seealso DMPlexRestoreClosureIndices(), DMPlexVecGetClosure(), DMPlexMatSetClosure(), DMGetLocalSection(), DMGetGlobalSection()
6113 @*/
6114 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscInt *numIndices, PetscInt **indices, PetscInt *outOffsets)
6115 {
6116   PetscBool       isLocal = (PetscBool)(section == idxSection);
6117   PetscSection    clSection;
6118   IS              clPoints;
6119   const PetscInt *clp, *clperm;
6120   const PetscInt  **perms[32] = {NULL};
6121   PetscInt       *points = NULL, *pointsNew;
6122   PetscInt        numPoints, numPointsNew;
6123   PetscInt        offsets[32];
6124   PetscInt        Nf, Nind, NindNew, off, idxOff, f, p;
6125   PetscErrorCode  ierr;
6126 
6127   PetscFunctionBegin;
6128   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6129   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6130   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
6131   if (numIndices) PetscValidPointer(numIndices, 4);
6132   PetscValidPointer(indices, 5);
6133   ierr = PetscSectionGetNumFields(section, &Nf);CHKERRQ(ierr);
6134   if (Nf > 31) SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %D limited to 31", Nf);
6135   ierr = PetscArrayzero(offsets, 32);CHKERRQ(ierr);
6136   /* Get points in closure */
6137   ierr = DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp);CHKERRQ(ierr);
6138   ierr = PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, NULL, &clperm);CHKERRQ(ierr);
6139   /* Get number of indices and indices per field */
6140   for (p = 0, Nind = 0; p < numPoints*2; p += 2) {
6141     PetscInt dof, fdof;
6142 
6143     ierr = PetscSectionGetDof(section, points[p], &dof);CHKERRQ(ierr);
6144     for (f = 0; f < Nf; ++f) {
6145       ierr = PetscSectionGetFieldDof(section, points[p], f, &fdof);CHKERRQ(ierr);
6146       offsets[f+1] += fdof;
6147     }
6148     Nind += dof;
6149   }
6150   for (f = 1; f < Nf; ++f) offsets[f+1] += offsets[f];
6151   if (Nf && offsets[Nf] != Nind) SETERRQ2(PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Invalid size for closure %D should be %D", offsets[Nf], Nind);
6152   if (!Nf) offsets[1] = Nind;
6153   /* Get dual space symmetries */
6154   for (f = 0; f < PetscMax(1,Nf); f++) {
6155     if (Nf) {ierr = PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms[f],NULL);CHKERRQ(ierr);}
6156     else    {ierr = PetscSectionGetPointSyms(section,numPoints,points,&perms[f],NULL);CHKERRQ(ierr);}
6157   }
6158   /* Correct for hanging node constraints */
6159   {
6160     ierr = DMPlexAnchorsModifyMat(dm, section, numPoints, Nind, points, perms, NULL, &numPointsNew, &NindNew, &pointsNew, NULL, offsets, PETSC_TRUE);CHKERRQ(ierr);
6161     if (numPointsNew) {
6162       for (f = 0; f < PetscMax(1,Nf); f++) {
6163         if (Nf) {ierr = PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms[f],NULL);CHKERRQ(ierr);}
6164         else    {ierr = PetscSectionRestorePointSyms(section,numPoints,points,&perms[f],NULL);CHKERRQ(ierr);}
6165       }
6166       for (f = 0; f < PetscMax(1,Nf); f++) {
6167         if (Nf) {ierr = PetscSectionGetFieldPointSyms(section,f,numPointsNew,pointsNew,&perms[f],NULL);CHKERRQ(ierr);}
6168         else    {ierr = PetscSectionGetPointSyms(section,numPointsNew,pointsNew,&perms[f],NULL);CHKERRQ(ierr);}
6169       }
6170       ierr = DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp);CHKERRQ(ierr);
6171       numPoints = numPointsNew;
6172       Nind      = NindNew;
6173       points    = pointsNew;
6174     }
6175   }
6176   /* Calculate indices */
6177   ierr = DMGetWorkArray(dm, Nind, MPIU_INT, indices);CHKERRQ(ierr);
6178   if (Nf) {
6179     if (outOffsets) {
6180       PetscInt f;
6181 
6182       for (f = 0; f <= Nf; f++) {
6183         outOffsets[f] = offsets[f];
6184       }
6185     }
6186     for (p = 0; p < numPoints; p++) {
6187       ierr = PetscSectionGetOffset(idxSection, points[2*p], &idxOff);CHKERRQ(ierr);
6188       ierr = DMPlexGetIndicesPointFields_Internal(section, isLocal, points[2*p], idxOff < 0 ? -(idxOff+1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, *indices);CHKERRQ(ierr);
6189     }
6190   } else {
6191     for (p = 0, off = 0; p < numPoints; p++) {
6192       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
6193 
6194       ierr = PetscSectionGetOffset(idxSection, points[2*p], &idxOff);CHKERRQ(ierr);
6195       ierr = DMPlexGetIndicesPoint_Internal(section, isLocal, points[2*p], idxOff < 0 ? -(idxOff+1) : idxOff, &off, PETSC_FALSE, perm, clperm, *indices);CHKERRQ(ierr);
6196     }
6197   }
6198   /* Cleanup points */
6199   for (f = 0; f < PetscMax(1,Nf); f++) {
6200     if (Nf) {ierr = PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms[f],NULL);CHKERRQ(ierr);}
6201     else    {ierr = PetscSectionRestorePointSyms(section,numPoints,points,&perms[f],NULL);CHKERRQ(ierr);}
6202   }
6203   if (numPointsNew) {
6204     ierr = DMRestoreWorkArray(dm, 2*numPointsNew, MPIU_INT, &pointsNew);CHKERRQ(ierr);
6205   } else {
6206     ierr = DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp);CHKERRQ(ierr);
6207   }
6208   if (numIndices) *numIndices = Nind;
6209   PetscFunctionReturn(0);
6210 }
6211 
6212 /*@C
6213   DMPlexRestoreClosureIndices - Restore the indices in a vector v for all points in the closure of the given point
6214 
6215   Not collective
6216 
6217   Input Parameters:
6218 + dm - The DM
6219 . section - The section describing the layout in v, or NULL to use the default section
6220 . globalSection - The section describing the parallel layout in v, or NULL to use the default section
6221 . point - The mesh point
6222 . numIndices - The number of indices
6223 . indices - The indices
6224 - outOffsets - Field offset if not NULL
6225 
6226   Level: advanced
6227 
6228 .seealso DMPlexGetClosureIndices(), DMPlexVecGetClosure(), DMPlexMatSetClosure()
6229 @*/
6230 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection globalSection, PetscInt point, PetscInt *numIndices, PetscInt **indices,PetscInt *outOffsets)
6231 {
6232   PetscErrorCode ierr;
6233 
6234   PetscFunctionBegin;
6235   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6236   PetscValidPointer(indices, 5);
6237   ierr = DMRestoreWorkArray(dm, 0, MPIU_INT, indices);CHKERRQ(ierr);
6238   PetscFunctionReturn(0);
6239 }
6240 
6241 /*@C
6242   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
6243 
6244   Not collective
6245 
6246   Input Parameters:
6247 + dm - The DM
6248 . section - The section describing the layout in v, or NULL to use the default section
6249 . globalSection - The section describing the layout in v, or NULL to use the default global section
6250 . A - The matrix
6251 . point - The point in the DM
6252 . values - The array of values
6253 - mode - The insert mode, where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions
6254 
6255   Fortran Notes:
6256   This routine is only available in Fortran 90, and you must include petsc.h90 in your code.
6257 
6258   Level: intermediate
6259 
6260 .seealso DMPlexVecGetClosure(), DMPlexVecSetClosure()
6261 @*/
6262 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
6263 {
6264   DM_Plex            *mesh   = (DM_Plex*) dm->data;
6265   PetscSection        clSection;
6266   IS                  clPoints;
6267   PetscInt           *points = NULL, *newPoints;
6268   const PetscInt     *clp, *clperm;
6269   PetscInt           *indices;
6270   PetscInt            offsets[32];
6271   const PetscInt    **perms[32] = {NULL};
6272   const PetscScalar **flips[32] = {NULL};
6273   PetscInt            numFields, numPoints, newNumPoints, numIndices, newNumIndices, dof, off, globalOff, p, f;
6274   PetscScalar        *valCopy = NULL;
6275   PetscScalar        *newValues;
6276   PetscErrorCode      ierr;
6277 
6278   PetscFunctionBegin;
6279   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6280   if (!section) {ierr = DMGetLocalSection(dm, &section);CHKERRQ(ierr);}
6281   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6282   if (!globalSection) {ierr = DMGetGlobalSection(dm, &globalSection);CHKERRQ(ierr);}
6283   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
6284   PetscValidHeaderSpecific(A, MAT_CLASSID, 4);
6285   ierr = PetscSectionGetNumFields(section, &numFields);CHKERRQ(ierr);
6286   if (numFields > 31) SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %D limited to 31", numFields);
6287   ierr = PetscArrayzero(offsets, 32);CHKERRQ(ierr);
6288   ierr = PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, NULL, &clperm);CHKERRQ(ierr);
6289   ierr = DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp);CHKERRQ(ierr);
6290   for (p = 0, numIndices = 0; p < numPoints*2; p += 2) {
6291     PetscInt fdof;
6292 
6293     ierr = PetscSectionGetDof(section, points[p], &dof);CHKERRQ(ierr);
6294     for (f = 0; f < numFields; ++f) {
6295       ierr          = PetscSectionGetFieldDof(section, points[p], f, &fdof);CHKERRQ(ierr);
6296       offsets[f+1] += fdof;
6297     }
6298     numIndices += dof;
6299   }
6300   for (f = 1; f < numFields; ++f) offsets[f+1] += offsets[f];
6301 
6302   if (numFields && offsets[numFields] != numIndices) SETERRQ2(PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %D should be %D", offsets[numFields], numIndices);
6303   /* Get symmetries */
6304   for (f = 0; f < PetscMax(1,numFields); f++) {
6305     if (numFields) {ierr = PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms[f],&flips[f]);CHKERRQ(ierr);}
6306     else           {ierr = PetscSectionGetPointSyms(section,numPoints,points,&perms[f],&flips[f]);CHKERRQ(ierr);}
6307     if (values && flips[f]) { /* may need to apply sign changes to the element matrix */
6308       PetscInt foffset = offsets[f];
6309 
6310       for (p = 0; p < numPoints; p++) {
6311         PetscInt point          = points[2*p], fdof;
6312         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
6313 
6314         if (!numFields) {
6315           ierr = PetscSectionGetDof(section,point,&fdof);CHKERRQ(ierr);
6316         } else {
6317           ierr = PetscSectionGetFieldDof(section,point,f,&fdof);CHKERRQ(ierr);
6318         }
6319         if (flip) {
6320           PetscInt i, j, k;
6321 
6322           if (!valCopy) {
6323             ierr = DMGetWorkArray(dm,numIndices*numIndices,MPIU_SCALAR,&valCopy);CHKERRQ(ierr);
6324             for (j = 0; j < numIndices * numIndices; j++) valCopy[j] = values[j];
6325             values = valCopy;
6326           }
6327           for (i = 0; i < fdof; i++) {
6328             PetscScalar fval = flip[i];
6329 
6330             for (k = 0; k < numIndices; k++) {
6331               valCopy[numIndices * (foffset + i) + k] *= fval;
6332               valCopy[numIndices * k + (foffset + i)] *= fval;
6333             }
6334           }
6335         }
6336         foffset += fdof;
6337       }
6338     }
6339   }
6340   ierr = DMPlexAnchorsModifyMat(dm,section,numPoints,numIndices,points,perms,values,&newNumPoints,&newNumIndices,&newPoints,&newValues,offsets,PETSC_TRUE);CHKERRQ(ierr);
6341   if (newNumPoints) {
6342     if (valCopy) {
6343       ierr = DMRestoreWorkArray(dm,numIndices*numIndices,MPIU_SCALAR,&valCopy);CHKERRQ(ierr);
6344     }
6345     for (f = 0; f < PetscMax(1,numFields); f++) {
6346       if (numFields) {ierr = PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms[f],&flips[f]);CHKERRQ(ierr);}
6347       else           {ierr = PetscSectionRestorePointSyms(section,numPoints,points,&perms[f],&flips[f]);CHKERRQ(ierr);}
6348     }
6349     for (f = 0; f < PetscMax(1,numFields); f++) {
6350       if (numFields) {ierr = PetscSectionGetFieldPointSyms(section,f,newNumPoints,newPoints,&perms[f],&flips[f]);CHKERRQ(ierr);}
6351       else           {ierr = PetscSectionGetPointSyms(section,newNumPoints,newPoints,&perms[f],&flips[f]);CHKERRQ(ierr);}
6352     }
6353     ierr = DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp);CHKERRQ(ierr);
6354     numPoints  = newNumPoints;
6355     numIndices = newNumIndices;
6356     points     = newPoints;
6357     values     = newValues;
6358   }
6359   ierr = DMGetWorkArray(dm, numIndices, MPIU_INT, &indices);CHKERRQ(ierr);
6360   if (numFields) {
6361     PetscBool useFieldOffsets;
6362 
6363     ierr = PetscSectionGetUseFieldOffsets(globalSection, &useFieldOffsets);CHKERRQ(ierr);
6364     if (useFieldOffsets) {
6365       for (p = 0; p < numPoints; p++) {
6366         ierr = DMPlexGetIndicesPointFieldsSplit_Internal(section, globalSection, points[2*p], offsets, perms, p, clperm, indices);CHKERRQ(ierr);
6367       }
6368     } else {
6369       for (p = 0; p < numPoints; p++) {
6370         ierr = PetscSectionGetOffset(globalSection, points[2*p], &globalOff);CHKERRQ(ierr);
6371         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
6372          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
6373          * global section. */
6374         ierr = DMPlexGetIndicesPointFields_Internal(section, PETSC_FALSE, points[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, offsets, PETSC_FALSE, perms, p, clperm, indices);CHKERRQ(ierr);
6375       }
6376     }
6377   } else {
6378     for (p = 0, off = 0; p < numPoints; p++) {
6379       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
6380       ierr = PetscSectionGetOffset(globalSection, points[2*p], &globalOff);CHKERRQ(ierr);
6381       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
6382        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
6383        * global section. */
6384       ierr = DMPlexGetIndicesPoint_Internal(section, PETSC_FALSE, points[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, clperm, indices);CHKERRQ(ierr);
6385     }
6386   }
6387   if (mesh->printSetValues) {ierr = DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values);CHKERRQ(ierr);}
6388   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);CHKERRQ(ierr);
6389   if (mesh->printFEM > 1) {
6390     PetscInt i;
6391     ierr = PetscPrintf(PETSC_COMM_SELF, "  Indices:");CHKERRQ(ierr);
6392     for (i = 0; i < numIndices; ++i) {ierr = PetscPrintf(PETSC_COMM_SELF, " %D", indices[i]);CHKERRQ(ierr);}
6393     ierr = PetscPrintf(PETSC_COMM_SELF, "\n");CHKERRQ(ierr);
6394   }
6395   if (ierr) {
6396     PetscMPIInt    rank;
6397     PetscErrorCode ierr2;
6398 
6399     ierr2 = MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank);CHKERRQ(ierr2);
6400     ierr2 = (*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank);CHKERRQ(ierr2);
6401     ierr2 = DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values);CHKERRQ(ierr2);
6402     ierr2 = DMRestoreWorkArray(dm, numIndices, MPIU_INT, &indices);CHKERRQ(ierr2);
6403     CHKERRQ(ierr);
6404   }
6405   for (f = 0; f < PetscMax(1,numFields); f++) {
6406     if (numFields) {ierr = PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms[f],&flips[f]);CHKERRQ(ierr);}
6407     else           {ierr = PetscSectionRestorePointSyms(section,numPoints,points,&perms[f],&flips[f]);CHKERRQ(ierr);}
6408   }
6409   if (newNumPoints) {
6410     ierr = DMRestoreWorkArray(dm,newNumIndices*newNumIndices,MPIU_SCALAR,&newValues);CHKERRQ(ierr);
6411     ierr = DMRestoreWorkArray(dm,2*newNumPoints,MPIU_INT,&newPoints);CHKERRQ(ierr);
6412   }
6413   else {
6414     if (valCopy) {
6415       ierr = DMRestoreWorkArray(dm,numIndices*numIndices,MPIU_SCALAR,&valCopy);CHKERRQ(ierr);
6416     }
6417     ierr = DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp);CHKERRQ(ierr);
6418   }
6419   ierr = DMRestoreWorkArray(dm, numIndices, MPIU_INT, &indices);CHKERRQ(ierr);
6420   PetscFunctionReturn(0);
6421 }
6422 
6423 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
6424 {
6425   DM_Plex        *mesh   = (DM_Plex*) dmf->data;
6426   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
6427   PetscInt       *cpoints = NULL;
6428   PetscInt       *findices, *cindices;
6429   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
6430   PetscInt        foffsets[32], coffsets[32];
6431   DMPolytopeType  ct;
6432   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
6433   PetscErrorCode  ierr;
6434 
6435   PetscFunctionBegin;
6436   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
6437   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
6438   if (!fsection) {ierr = DMGetLocalSection(dmf, &fsection);CHKERRQ(ierr);}
6439   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
6440   if (!csection) {ierr = DMGetLocalSection(dmc, &csection);CHKERRQ(ierr);}
6441   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
6442   if (!globalFSection) {ierr = DMGetGlobalSection(dmf, &globalFSection);CHKERRQ(ierr);}
6443   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
6444   if (!globalCSection) {ierr = DMGetGlobalSection(dmc, &globalCSection);CHKERRQ(ierr);}
6445   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
6446   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
6447   ierr = PetscSectionGetNumFields(fsection, &numFields);CHKERRQ(ierr);
6448   if (numFields > 31) SETERRQ1(PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %D limited to 31", numFields);
6449   ierr = PetscArrayzero(foffsets, 32);CHKERRQ(ierr);
6450   ierr = PetscArrayzero(coffsets, 32);CHKERRQ(ierr);
6451   /* Column indices */
6452   ierr = DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints);CHKERRQ(ierr);
6453   maxFPoints = numCPoints;
6454   /* Compress out points not in the section */
6455   /*   TODO: Squeeze out points with 0 dof as well */
6456   ierr = PetscSectionGetChart(csection, &pStart, &pEnd);CHKERRQ(ierr);
6457   for (p = 0, q = 0; p < numCPoints*2; p += 2) {
6458     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
6459       cpoints[q*2]   = cpoints[p];
6460       cpoints[q*2+1] = cpoints[p+1];
6461       ++q;
6462     }
6463   }
6464   numCPoints = q;
6465   for (p = 0, numCIndices = 0; p < numCPoints*2; p += 2) {
6466     PetscInt fdof;
6467 
6468     ierr = PetscSectionGetDof(csection, cpoints[p], &dof);CHKERRQ(ierr);
6469     if (!dof) continue;
6470     for (f = 0; f < numFields; ++f) {
6471       ierr           = PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof);CHKERRQ(ierr);
6472       coffsets[f+1] += fdof;
6473     }
6474     numCIndices += dof;
6475   }
6476   for (f = 1; f < numFields; ++f) coffsets[f+1] += coffsets[f];
6477   /* Row indices */
6478   ierr = DMPlexGetCellType(dmc, point, &ct);CHKERRQ(ierr);
6479   {
6480     DMPlexCellRefiner cr;
6481     ierr = DMPlexCellRefinerCreate(dmc, &cr);CHKERRQ(ierr);
6482     ierr = DMPlexCellRefinerGetAffineTransforms(cr, ct, &numSubcells, NULL, NULL, NULL);CHKERRQ(ierr);
6483     ierr = DMPlexCellRefinerDestroy(&cr);CHKERRQ(ierr);
6484   }
6485   ierr = DMGetWorkArray(dmf, maxFPoints*2*numSubcells, MPIU_INT, &ftotpoints);CHKERRQ(ierr);
6486   for (r = 0, q = 0; r < numSubcells; ++r) {
6487     /* TODO Map from coarse to fine cells */
6488     ierr = DMPlexGetTransitiveClosure(dmf, point*numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints);CHKERRQ(ierr);
6489     /* Compress out points not in the section */
6490     ierr = PetscSectionGetChart(fsection, &pStart, &pEnd);CHKERRQ(ierr);
6491     for (p = 0; p < numFPoints*2; p += 2) {
6492       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
6493         ierr = PetscSectionGetDof(fsection, fpoints[p], &dof);CHKERRQ(ierr);
6494         if (!dof) continue;
6495         for (s = 0; s < q; ++s) if (fpoints[p] == ftotpoints[s*2]) break;
6496         if (s < q) continue;
6497         ftotpoints[q*2]   = fpoints[p];
6498         ftotpoints[q*2+1] = fpoints[p+1];
6499         ++q;
6500       }
6501     }
6502     ierr = DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints);CHKERRQ(ierr);
6503   }
6504   numFPoints = q;
6505   for (p = 0, numFIndices = 0; p < numFPoints*2; p += 2) {
6506     PetscInt fdof;
6507 
6508     ierr = PetscSectionGetDof(fsection, ftotpoints[p], &dof);CHKERRQ(ierr);
6509     if (!dof) continue;
6510     for (f = 0; f < numFields; ++f) {
6511       ierr           = PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof);CHKERRQ(ierr);
6512       foffsets[f+1] += fdof;
6513     }
6514     numFIndices += dof;
6515   }
6516   for (f = 1; f < numFields; ++f) foffsets[f+1] += foffsets[f];
6517 
6518   if (numFields && foffsets[numFields] != numFIndices) SETERRQ2(PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %D should be %D", foffsets[numFields], numFIndices);
6519   if (numFields && coffsets[numFields] != numCIndices) SETERRQ2(PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %D should be %D", coffsets[numFields], numCIndices);
6520   ierr = DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices);CHKERRQ(ierr);
6521   ierr = DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices);CHKERRQ(ierr);
6522   if (numFields) {
6523     const PetscInt **permsF[32] = {NULL};
6524     const PetscInt **permsC[32] = {NULL};
6525 
6526     for (f = 0; f < numFields; f++) {
6527       ierr = PetscSectionGetFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL);CHKERRQ(ierr);
6528       ierr = PetscSectionGetFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL);CHKERRQ(ierr);
6529     }
6530     for (p = 0; p < numFPoints; p++) {
6531       ierr = PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff);CHKERRQ(ierr);
6532       ierr = DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices);CHKERRQ(ierr);
6533     }
6534     for (p = 0; p < numCPoints; p++) {
6535       ierr = PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff);CHKERRQ(ierr);
6536       ierr = DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices);CHKERRQ(ierr);
6537     }
6538     for (f = 0; f < numFields; f++) {
6539       ierr = PetscSectionRestoreFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL);CHKERRQ(ierr);
6540       ierr = PetscSectionRestoreFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL);CHKERRQ(ierr);
6541     }
6542   } else {
6543     const PetscInt **permsF = NULL;
6544     const PetscInt **permsC = NULL;
6545 
6546     ierr = PetscSectionGetPointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL);CHKERRQ(ierr);
6547     ierr = PetscSectionGetPointSyms(csection,numCPoints,cpoints,&permsC,NULL);CHKERRQ(ierr);
6548     for (p = 0, off = 0; p < numFPoints; p++) {
6549       const PetscInt *perm = permsF ? permsF[p] : NULL;
6550 
6551       ierr = PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff);CHKERRQ(ierr);
6552       ierr = DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices);CHKERRQ(ierr);
6553     }
6554     for (p = 0, off = 0; p < numCPoints; p++) {
6555       const PetscInt *perm = permsC ? permsC[p] : NULL;
6556 
6557       ierr = PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff);CHKERRQ(ierr);
6558       ierr = DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices);CHKERRQ(ierr);
6559     }
6560     ierr = PetscSectionRestorePointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL);CHKERRQ(ierr);
6561     ierr = PetscSectionRestorePointSyms(csection,numCPoints,cpoints,&permsC,NULL);CHKERRQ(ierr);
6562   }
6563   if (mesh->printSetValues) {ierr = DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values);CHKERRQ(ierr);}
6564   /* TODO: flips */
6565   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
6566   if (ierr) {
6567     PetscMPIInt    rank;
6568     PetscErrorCode ierr2;
6569 
6570     ierr2 = MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank);CHKERRQ(ierr2);
6571     ierr2 = (*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank);CHKERRQ(ierr2);
6572     ierr2 = DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values);CHKERRQ(ierr2);
6573     ierr2 = DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices);CHKERRQ(ierr2);
6574     ierr2 = DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices);CHKERRQ(ierr2);
6575     CHKERRQ(ierr);
6576   }
6577   ierr = DMRestoreWorkArray(dmf, numCPoints*2*4, MPIU_INT, &ftotpoints);CHKERRQ(ierr);
6578   ierr = DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints);CHKERRQ(ierr);
6579   ierr = DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices);CHKERRQ(ierr);
6580   ierr = DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices);CHKERRQ(ierr);
6581   PetscFunctionReturn(0);
6582 }
6583 
6584 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
6585 {
6586   PetscInt      *fpoints = NULL, *ftotpoints = NULL;
6587   PetscInt      *cpoints = NULL;
6588   PetscInt       foffsets[32], coffsets[32];
6589   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
6590   DMPolytopeType ct;
6591   PetscInt       numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
6592   PetscErrorCode ierr;
6593 
6594   PetscFunctionBegin;
6595   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
6596   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
6597   if (!fsection) {ierr = DMGetLocalSection(dmf, &fsection);CHKERRQ(ierr);}
6598   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
6599   if (!csection) {ierr = DMGetLocalSection(dmc, &csection);CHKERRQ(ierr);}
6600   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
6601   if (!globalFSection) {ierr = DMGetGlobalSection(dmf, &globalFSection);CHKERRQ(ierr);}
6602   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
6603   if (!globalCSection) {ierr = DMGetGlobalSection(dmc, &globalCSection);CHKERRQ(ierr);}
6604   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
6605   ierr = PetscSectionGetNumFields(fsection, &numFields);CHKERRQ(ierr);
6606   if (numFields > 31) SETERRQ1(PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %D limited to 31", numFields);
6607   ierr = PetscArrayzero(foffsets, 32);CHKERRQ(ierr);
6608   ierr = PetscArrayzero(coffsets, 32);CHKERRQ(ierr);
6609   /* Column indices */
6610   ierr = DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints);CHKERRQ(ierr);
6611   maxFPoints = numCPoints;
6612   /* Compress out points not in the section */
6613   /*   TODO: Squeeze out points with 0 dof as well */
6614   ierr = PetscSectionGetChart(csection, &pStart, &pEnd);CHKERRQ(ierr);
6615   for (p = 0, q = 0; p < numCPoints*2; p += 2) {
6616     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
6617       cpoints[q*2]   = cpoints[p];
6618       cpoints[q*2+1] = cpoints[p+1];
6619       ++q;
6620     }
6621   }
6622   numCPoints = q;
6623   for (p = 0, numCIndices = 0; p < numCPoints*2; p += 2) {
6624     PetscInt fdof;
6625 
6626     ierr = PetscSectionGetDof(csection, cpoints[p], &dof);CHKERRQ(ierr);
6627     if (!dof) continue;
6628     for (f = 0; f < numFields; ++f) {
6629       ierr           = PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof);CHKERRQ(ierr);
6630       coffsets[f+1] += fdof;
6631     }
6632     numCIndices += dof;
6633   }
6634   for (f = 1; f < numFields; ++f) coffsets[f+1] += coffsets[f];
6635   /* Row indices */
6636   ierr = DMPlexGetCellType(dmc, point, &ct);CHKERRQ(ierr);
6637   {
6638     DMPlexCellRefiner cr;
6639     ierr = DMPlexCellRefinerCreate(dmc, &cr);CHKERRQ(ierr);
6640     ierr = DMPlexCellRefinerGetAffineTransforms(cr, ct, &numSubcells, NULL, NULL, NULL);CHKERRQ(ierr);
6641     ierr = DMPlexCellRefinerDestroy(&cr);CHKERRQ(ierr);
6642   }
6643   ierr = DMGetWorkArray(dmf, maxFPoints*2*numSubcells, MPIU_INT, &ftotpoints);CHKERRQ(ierr);
6644   for (r = 0, q = 0; r < numSubcells; ++r) {
6645     /* TODO Map from coarse to fine cells */
6646     ierr = DMPlexGetTransitiveClosure(dmf, point*numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints);CHKERRQ(ierr);
6647     /* Compress out points not in the section */
6648     ierr = PetscSectionGetChart(fsection, &pStart, &pEnd);CHKERRQ(ierr);
6649     for (p = 0; p < numFPoints*2; p += 2) {
6650       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
6651         ierr = PetscSectionGetDof(fsection, fpoints[p], &dof);CHKERRQ(ierr);
6652         if (!dof) continue;
6653         for (s = 0; s < q; ++s) if (fpoints[p] == ftotpoints[s*2]) break;
6654         if (s < q) continue;
6655         ftotpoints[q*2]   = fpoints[p];
6656         ftotpoints[q*2+1] = fpoints[p+1];
6657         ++q;
6658       }
6659     }
6660     ierr = DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints);CHKERRQ(ierr);
6661   }
6662   numFPoints = q;
6663   for (p = 0, numFIndices = 0; p < numFPoints*2; p += 2) {
6664     PetscInt fdof;
6665 
6666     ierr = PetscSectionGetDof(fsection, ftotpoints[p], &dof);CHKERRQ(ierr);
6667     if (!dof) continue;
6668     for (f = 0; f < numFields; ++f) {
6669       ierr           = PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof);CHKERRQ(ierr);
6670       foffsets[f+1] += fdof;
6671     }
6672     numFIndices += dof;
6673   }
6674   for (f = 1; f < numFields; ++f) foffsets[f+1] += foffsets[f];
6675 
6676   if (numFields && foffsets[numFields] != numFIndices) SETERRQ2(PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %D should be %D", foffsets[numFields], numFIndices);
6677   if (numFields && coffsets[numFields] != numCIndices) SETERRQ2(PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %D should be %D", coffsets[numFields], numCIndices);
6678   if (numFields) {
6679     const PetscInt **permsF[32] = {NULL};
6680     const PetscInt **permsC[32] = {NULL};
6681 
6682     for (f = 0; f < numFields; f++) {
6683       ierr = PetscSectionGetFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL);CHKERRQ(ierr);
6684       ierr = PetscSectionGetFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL);CHKERRQ(ierr);
6685     }
6686     for (p = 0; p < numFPoints; p++) {
6687       ierr = PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff);CHKERRQ(ierr);
6688       ierr = DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices);CHKERRQ(ierr);
6689     }
6690     for (p = 0; p < numCPoints; p++) {
6691       ierr = PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff);CHKERRQ(ierr);
6692       ierr = DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices);CHKERRQ(ierr);
6693     }
6694     for (f = 0; f < numFields; f++) {
6695       ierr = PetscSectionRestoreFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL);CHKERRQ(ierr);
6696       ierr = PetscSectionRestoreFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL);CHKERRQ(ierr);
6697     }
6698   } else {
6699     const PetscInt **permsF = NULL;
6700     const PetscInt **permsC = NULL;
6701 
6702     ierr = PetscSectionGetPointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL);CHKERRQ(ierr);
6703     ierr = PetscSectionGetPointSyms(csection,numCPoints,cpoints,&permsC,NULL);CHKERRQ(ierr);
6704     for (p = 0, off = 0; p < numFPoints; p++) {
6705       const PetscInt *perm = permsF ? permsF[p] : NULL;
6706 
6707       ierr = PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff);CHKERRQ(ierr);
6708       ierr = DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices);CHKERRQ(ierr);
6709     }
6710     for (p = 0, off = 0; p < numCPoints; p++) {
6711       const PetscInt *perm = permsC ? permsC[p] : NULL;
6712 
6713       ierr = PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff);CHKERRQ(ierr);
6714       ierr = DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices);CHKERRQ(ierr);
6715     }
6716     ierr = PetscSectionRestorePointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL);CHKERRQ(ierr);
6717     ierr = PetscSectionRestorePointSyms(csection,numCPoints,cpoints,&permsC,NULL);CHKERRQ(ierr);
6718   }
6719   ierr = DMRestoreWorkArray(dmf, numCPoints*2*4, MPIU_INT, &ftotpoints);CHKERRQ(ierr);
6720   ierr = DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints);CHKERRQ(ierr);
6721   PetscFunctionReturn(0);
6722 }
6723 
6724 /*@C
6725   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
6726 
6727   Input Parameter:
6728 . dm   - The DMPlex object
6729 
6730   Output Parameter:
6731 . cellHeight - The height of a cell
6732 
6733   Level: developer
6734 
6735 .seealso DMPlexSetVTKCellHeight()
6736 @*/
6737 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
6738 {
6739   DM_Plex *mesh = (DM_Plex*) dm->data;
6740 
6741   PetscFunctionBegin;
6742   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6743   PetscValidPointer(cellHeight, 2);
6744   *cellHeight = mesh->vtkCellHeight;
6745   PetscFunctionReturn(0);
6746 }
6747 
6748 /*@C
6749   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
6750 
6751   Input Parameters:
6752 + dm   - The DMPlex object
6753 - cellHeight - The height of a cell
6754 
6755   Level: developer
6756 
6757 .seealso DMPlexGetVTKCellHeight()
6758 @*/
6759 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
6760 {
6761   DM_Plex *mesh = (DM_Plex*) dm->data;
6762 
6763   PetscFunctionBegin;
6764   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6765   mesh->vtkCellHeight = cellHeight;
6766   PetscFunctionReturn(0);
6767 }
6768 
6769 /*@
6770   DMPlexGetGhostCellStratum - Get the range of cells which are used to enforce FV boundary conditions
6771 
6772   Input Parameter:
6773 . dm - The DMPlex object
6774 
6775   Output Parameters:
6776 + gcStart - The first ghost cell, or NULL
6777 - gcEnd   - The upper bound on ghost cells, or NULL
6778 
6779   Level: advanced
6780 
6781 .seealso DMPlexConstructGhostCells(), DMPlexSetGhostCellStratum()
6782 @*/
6783 PetscErrorCode DMPlexGetGhostCellStratum(DM dm, PetscInt *gcStart, PetscInt *gcEnd)
6784 {
6785   DMLabel        ctLabel;
6786   PetscErrorCode ierr;
6787 
6788   PetscFunctionBegin;
6789   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6790   ierr = DMPlexGetCellTypeLabel(dm, &ctLabel);CHKERRQ(ierr);
6791   ierr = DMLabelGetStratumBounds(ctLabel, DM_POLYTOPE_FV_GHOST, gcStart, gcEnd);CHKERRQ(ierr);
6792   PetscFunctionReturn(0);
6793 }
6794 
6795 /* We can easily have a form that takes an IS instead */
6796 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
6797 {
6798   PetscSection   section, globalSection;
6799   PetscInt      *numbers, p;
6800   PetscErrorCode ierr;
6801 
6802   PetscFunctionBegin;
6803   ierr = PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section);CHKERRQ(ierr);
6804   ierr = PetscSectionSetChart(section, pStart, pEnd);CHKERRQ(ierr);
6805   for (p = pStart; p < pEnd; ++p) {
6806     ierr = PetscSectionSetDof(section, p, 1);CHKERRQ(ierr);
6807   }
6808   ierr = PetscSectionSetUp(section);CHKERRQ(ierr);
6809   ierr = PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection);CHKERRQ(ierr);
6810   ierr = PetscMalloc1(pEnd - pStart, &numbers);CHKERRQ(ierr);
6811   for (p = pStart; p < pEnd; ++p) {
6812     ierr = PetscSectionGetOffset(globalSection, p, &numbers[p-pStart]);CHKERRQ(ierr);
6813     if (numbers[p-pStart] < 0) numbers[p-pStart] -= shift;
6814     else                       numbers[p-pStart] += shift;
6815   }
6816   ierr = ISCreateGeneral(PetscObjectComm((PetscObject) dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering);CHKERRQ(ierr);
6817   if (globalSize) {
6818     PetscLayout layout;
6819     ierr = PetscSectionGetPointLayout(PetscObjectComm((PetscObject) dm), globalSection, &layout);CHKERRQ(ierr);
6820     ierr = PetscLayoutGetSize(layout, globalSize);CHKERRQ(ierr);
6821     ierr = PetscLayoutDestroy(&layout);CHKERRQ(ierr);
6822   }
6823   ierr = PetscSectionDestroy(&section);CHKERRQ(ierr);
6824   ierr = PetscSectionDestroy(&globalSection);CHKERRQ(ierr);
6825   PetscFunctionReturn(0);
6826 }
6827 
6828 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
6829 {
6830   PetscInt       cellHeight, cStart, cEnd;
6831   PetscErrorCode ierr;
6832 
6833   PetscFunctionBegin;
6834   ierr = DMPlexGetVTKCellHeight(dm, &cellHeight);CHKERRQ(ierr);
6835   if (includeHybrid) {ierr = DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd);CHKERRQ(ierr);}
6836   else               {ierr = DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd);CHKERRQ(ierr);}
6837   ierr = DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers);CHKERRQ(ierr);
6838   PetscFunctionReturn(0);
6839 }
6840 
6841 /*@
6842   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
6843 
6844   Input Parameter:
6845 . dm   - The DMPlex object
6846 
6847   Output Parameter:
6848 . globalCellNumbers - Global cell numbers for all cells on this process
6849 
6850   Level: developer
6851 
6852 .seealso DMPlexGetVertexNumbering()
6853 @*/
6854 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
6855 {
6856   DM_Plex       *mesh = (DM_Plex*) dm->data;
6857   PetscErrorCode ierr;
6858 
6859   PetscFunctionBegin;
6860   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6861   if (!mesh->globalCellNumbers) {ierr = DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers);CHKERRQ(ierr);}
6862   *globalCellNumbers = mesh->globalCellNumbers;
6863   PetscFunctionReturn(0);
6864 }
6865 
6866 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
6867 {
6868   PetscInt       vStart, vEnd;
6869   PetscErrorCode ierr;
6870 
6871   PetscFunctionBegin;
6872   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6873   ierr = DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd);CHKERRQ(ierr);
6874   ierr = DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers);CHKERRQ(ierr);
6875   PetscFunctionReturn(0);
6876 }
6877 
6878 /*@
6879   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
6880 
6881   Input Parameter:
6882 . dm   - The DMPlex object
6883 
6884   Output Parameter:
6885 . globalVertexNumbers - Global vertex numbers for all vertices on this process
6886 
6887   Level: developer
6888 
6889 .seealso DMPlexGetCellNumbering()
6890 @*/
6891 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
6892 {
6893   DM_Plex       *mesh = (DM_Plex*) dm->data;
6894   PetscErrorCode ierr;
6895 
6896   PetscFunctionBegin;
6897   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6898   if (!mesh->globalVertexNumbers) {ierr = DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers);CHKERRQ(ierr);}
6899   *globalVertexNumbers = mesh->globalVertexNumbers;
6900   PetscFunctionReturn(0);
6901 }
6902 
6903 /*@
6904   DMPlexCreatePointNumbering - Create a global numbering for all points on this process
6905 
6906   Input Parameter:
6907 . dm   - The DMPlex object
6908 
6909   Output Parameter:
6910 . globalPointNumbers - Global numbers for all points on this process
6911 
6912   Level: developer
6913 
6914 .seealso DMPlexGetCellNumbering()
6915 @*/
6916 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
6917 {
6918   IS             nums[4];
6919   PetscInt       depths[4], gdepths[4], starts[4];
6920   PetscInt       depth, d, shift = 0;
6921   PetscErrorCode ierr;
6922 
6923   PetscFunctionBegin;
6924   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6925   ierr = DMPlexGetDepth(dm, &depth);CHKERRQ(ierr);
6926   /* For unstratified meshes use dim instead of depth */
6927   if (depth < 0) {ierr = DMGetDimension(dm, &depth);CHKERRQ(ierr);}
6928   for (d = 0; d <= depth; ++d) {
6929     PetscInt end;
6930 
6931     depths[d] = depth-d;
6932     ierr = DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end);CHKERRQ(ierr);
6933     if (!(starts[d]-end)) { starts[d] = depths[d] = -1; }
6934   }
6935   ierr = PetscSortIntWithArray(depth+1, starts, depths);CHKERRQ(ierr);
6936   ierr = MPIU_Allreduce(depths, gdepths, depth+1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject) dm));CHKERRQ(ierr);
6937   for (d = 0; d <= depth; ++d) {
6938     if (starts[d] >= 0 && depths[d] != gdepths[d]) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_PLIB,"Expected depth %D, found %D",depths[d],gdepths[d]);
6939   }
6940   for (d = 0; d <= depth; ++d) {
6941     PetscInt pStart, pEnd, gsize;
6942 
6943     ierr = DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd);CHKERRQ(ierr);
6944     ierr = DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]);CHKERRQ(ierr);
6945     shift += gsize;
6946   }
6947   ierr = ISConcatenate(PetscObjectComm((PetscObject) dm), depth+1, nums, globalPointNumbers);CHKERRQ(ierr);
6948   for (d = 0; d <= depth; ++d) {ierr = ISDestroy(&nums[d]);CHKERRQ(ierr);}
6949   PetscFunctionReturn(0);
6950 }
6951 
6952 
6953 /*@
6954   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
6955 
6956   Input Parameter:
6957 . dm - The DMPlex object
6958 
6959   Output Parameter:
6960 . ranks - The rank field
6961 
6962   Options Database Keys:
6963 . -dm_partition_view - Adds the rank field into the DM output from -dm_view using the same viewer
6964 
6965   Level: intermediate
6966 
6967 .seealso: DMView()
6968 @*/
6969 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
6970 {
6971   DM             rdm;
6972   PetscFE        fe;
6973   PetscScalar   *r;
6974   PetscMPIInt    rank;
6975   PetscInt       dim, cStart, cEnd, c;
6976   PetscErrorCode ierr;
6977 
6978   PetscFunctionBeginUser;
6979   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6980   PetscValidPointer(ranks, 2);
6981   ierr = MPI_Comm_rank(PetscObjectComm((PetscObject) dm), &rank);CHKERRQ(ierr);
6982   ierr = DMClone(dm, &rdm);CHKERRQ(ierr);
6983   ierr = DMGetDimension(rdm, &dim);CHKERRQ(ierr);
6984   ierr = PetscFECreateDefault(PetscObjectComm((PetscObject) rdm), dim, 1, PETSC_TRUE, "PETSc___rank_", -1, &fe);CHKERRQ(ierr);
6985   ierr = PetscObjectSetName((PetscObject) fe, "rank");CHKERRQ(ierr);
6986   ierr = DMSetField(rdm, 0, NULL, (PetscObject) fe);CHKERRQ(ierr);
6987   ierr = PetscFEDestroy(&fe);CHKERRQ(ierr);
6988   ierr = DMCreateDS(rdm);CHKERRQ(ierr);
6989   ierr = DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd);CHKERRQ(ierr);
6990   ierr = DMCreateGlobalVector(rdm, ranks);CHKERRQ(ierr);
6991   ierr = PetscObjectSetName((PetscObject) *ranks, "partition");CHKERRQ(ierr);
6992   ierr = VecGetArray(*ranks, &r);CHKERRQ(ierr);
6993   for (c = cStart; c < cEnd; ++c) {
6994     PetscScalar *lr;
6995 
6996     ierr = DMPlexPointGlobalRef(rdm, c, r, &lr);CHKERRQ(ierr);
6997     if (lr) *lr = rank;
6998   }
6999   ierr = VecRestoreArray(*ranks, &r);CHKERRQ(ierr);
7000   ierr = DMDestroy(&rdm);CHKERRQ(ierr);
7001   PetscFunctionReturn(0);
7002 }
7003 
7004 /*@
7005   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
7006 
7007   Input Parameters:
7008 + dm    - The DMPlex
7009 - label - The DMLabel
7010 
7011   Output Parameter:
7012 . val - The label value field
7013 
7014   Options Database Keys:
7015 . -dm_label_view - Adds the label value field into the DM output from -dm_view using the same viewer
7016 
7017   Level: intermediate
7018 
7019 .seealso: DMView()
7020 @*/
7021 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
7022 {
7023   DM             rdm;
7024   PetscFE        fe;
7025   PetscScalar   *v;
7026   PetscInt       dim, cStart, cEnd, c;
7027   PetscErrorCode ierr;
7028 
7029   PetscFunctionBeginUser;
7030   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7031   PetscValidPointer(label, 2);
7032   PetscValidPointer(val, 3);
7033   ierr = DMClone(dm, &rdm);CHKERRQ(ierr);
7034   ierr = DMGetDimension(rdm, &dim);CHKERRQ(ierr);
7035   ierr = PetscFECreateDefault(PetscObjectComm((PetscObject) rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe);CHKERRQ(ierr);
7036   ierr = PetscObjectSetName((PetscObject) fe, "label_value");CHKERRQ(ierr);
7037   ierr = DMSetField(rdm, 0, NULL, (PetscObject) fe);CHKERRQ(ierr);
7038   ierr = PetscFEDestroy(&fe);CHKERRQ(ierr);
7039   ierr = DMCreateDS(rdm);CHKERRQ(ierr);
7040   ierr = DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd);CHKERRQ(ierr);
7041   ierr = DMCreateGlobalVector(rdm, val);CHKERRQ(ierr);
7042   ierr = PetscObjectSetName((PetscObject) *val, "label_value");CHKERRQ(ierr);
7043   ierr = VecGetArray(*val, &v);CHKERRQ(ierr);
7044   for (c = cStart; c < cEnd; ++c) {
7045     PetscScalar *lv;
7046     PetscInt     cval;
7047 
7048     ierr = DMPlexPointGlobalRef(rdm, c, v, &lv);CHKERRQ(ierr);
7049     ierr = DMLabelGetValue(label, c, &cval);CHKERRQ(ierr);
7050     *lv = cval;
7051   }
7052   ierr = VecRestoreArray(*val, &v);CHKERRQ(ierr);
7053   ierr = DMDestroy(&rdm);CHKERRQ(ierr);
7054   PetscFunctionReturn(0);
7055 }
7056 
7057 /*@
7058   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
7059 
7060   Input Parameter:
7061 . dm - The DMPlex object
7062 
7063   Notes:
7064   This is a useful diagnostic when creating meshes programmatically.
7065 
7066   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
7067 
7068   Level: developer
7069 
7070 .seealso: DMCreate(), DMSetFromOptions()
7071 @*/
7072 PetscErrorCode DMPlexCheckSymmetry(DM dm)
7073 {
7074   PetscSection    coneSection, supportSection;
7075   const PetscInt *cone, *support;
7076   PetscInt        coneSize, c, supportSize, s;
7077   PetscInt        pStart, pEnd, p, pp, csize, ssize;
7078   PetscBool       storagecheck = PETSC_TRUE;
7079   PetscErrorCode  ierr;
7080 
7081   PetscFunctionBegin;
7082   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7083   ierr = DMViewFromOptions(dm, NULL, "-sym_dm_view");CHKERRQ(ierr);
7084   ierr = DMPlexGetConeSection(dm, &coneSection);CHKERRQ(ierr);
7085   ierr = DMPlexGetSupportSection(dm, &supportSection);CHKERRQ(ierr);
7086   /* Check that point p is found in the support of its cone points, and vice versa */
7087   ierr = DMPlexGetChart(dm, &pStart, &pEnd);CHKERRQ(ierr);
7088   for (p = pStart; p < pEnd; ++p) {
7089     ierr = DMPlexGetConeSize(dm, p, &coneSize);CHKERRQ(ierr);
7090     ierr = DMPlexGetCone(dm, p, &cone);CHKERRQ(ierr);
7091     for (c = 0; c < coneSize; ++c) {
7092       PetscBool dup = PETSC_FALSE;
7093       PetscInt  d;
7094       for (d = c-1; d >= 0; --d) {
7095         if (cone[c] == cone[d]) {dup = PETSC_TRUE; break;}
7096       }
7097       ierr = DMPlexGetSupportSize(dm, cone[c], &supportSize);CHKERRQ(ierr);
7098       ierr = DMPlexGetSupport(dm, cone[c], &support);CHKERRQ(ierr);
7099       for (s = 0; s < supportSize; ++s) {
7100         if (support[s] == p) break;
7101       }
7102       if ((s >= supportSize) || (dup && (support[s+1] != p))) {
7103         ierr = PetscPrintf(PETSC_COMM_SELF, "p: %D cone: ", p);CHKERRQ(ierr);
7104         for (s = 0; s < coneSize; ++s) {
7105           ierr = PetscPrintf(PETSC_COMM_SELF, "%D, ", cone[s]);CHKERRQ(ierr);
7106         }
7107         ierr = PetscPrintf(PETSC_COMM_SELF, "\n");CHKERRQ(ierr);
7108         ierr = PetscPrintf(PETSC_COMM_SELF, "p: %D support: ", cone[c]);CHKERRQ(ierr);
7109         for (s = 0; s < supportSize; ++s) {
7110           ierr = PetscPrintf(PETSC_COMM_SELF, "%D, ", support[s]);CHKERRQ(ierr);
7111         }
7112         ierr = PetscPrintf(PETSC_COMM_SELF, "\n");CHKERRQ(ierr);
7113         if (dup) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %D not repeatedly found in support of repeated cone point %D", p, cone[c]);
7114         else SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %D not found in support of cone point %D", p, cone[c]);
7115       }
7116     }
7117     ierr = DMPlexGetTreeParent(dm, p, &pp, NULL);CHKERRQ(ierr);
7118     if (p != pp) { storagecheck = PETSC_FALSE; continue; }
7119     ierr = DMPlexGetSupportSize(dm, p, &supportSize);CHKERRQ(ierr);
7120     ierr = DMPlexGetSupport(dm, p, &support);CHKERRQ(ierr);
7121     for (s = 0; s < supportSize; ++s) {
7122       ierr = DMPlexGetConeSize(dm, support[s], &coneSize);CHKERRQ(ierr);
7123       ierr = DMPlexGetCone(dm, support[s], &cone);CHKERRQ(ierr);
7124       for (c = 0; c < coneSize; ++c) {
7125         ierr = DMPlexGetTreeParent(dm, cone[c], &pp, NULL);CHKERRQ(ierr);
7126         if (cone[c] != pp) { c = 0; break; }
7127         if (cone[c] == p) break;
7128       }
7129       if (c >= coneSize) {
7130         ierr = PetscPrintf(PETSC_COMM_SELF, "p: %D support: ", p);CHKERRQ(ierr);
7131         for (c = 0; c < supportSize; ++c) {
7132           ierr = PetscPrintf(PETSC_COMM_SELF, "%D, ", support[c]);CHKERRQ(ierr);
7133         }
7134         ierr = PetscPrintf(PETSC_COMM_SELF, "\n");CHKERRQ(ierr);
7135         ierr = PetscPrintf(PETSC_COMM_SELF, "p: %D cone: ", support[s]);CHKERRQ(ierr);
7136         for (c = 0; c < coneSize; ++c) {
7137           ierr = PetscPrintf(PETSC_COMM_SELF, "%D, ", cone[c]);CHKERRQ(ierr);
7138         }
7139         ierr = PetscPrintf(PETSC_COMM_SELF, "\n");CHKERRQ(ierr);
7140         SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %D not found in cone of support point %D", p, support[s]);
7141       }
7142     }
7143   }
7144   if (storagecheck) {
7145     ierr = PetscSectionGetStorageSize(coneSection, &csize);CHKERRQ(ierr);
7146     ierr = PetscSectionGetStorageSize(supportSection, &ssize);CHKERRQ(ierr);
7147     if (csize != ssize) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %D != Total support size %D", csize, ssize);
7148   }
7149   PetscFunctionReturn(0);
7150 }
7151 
7152 /*
7153   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.
7154 */
7155 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
7156 {
7157   DMPolytopeType  cct;
7158   PetscInt        ptpoints[4];
7159   const PetscInt *cone, *ccone, *ptcone;
7160   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
7161   PetscErrorCode  ierr;
7162 
7163   PetscFunctionBegin;
7164   *unsplit = 0;
7165   switch (ct) {
7166     case DM_POLYTOPE_SEG_PRISM_TENSOR:
7167       ierr = DMPlexGetCone(dm, c, &cone);CHKERRQ(ierr);
7168       ierr = DMPlexGetConeSize(dm, c, &coneSize);CHKERRQ(ierr);
7169       for (cp = 0; cp < coneSize; ++cp) {
7170         ierr = DMPlexGetCellType(dm, cone[cp], &cct);CHKERRQ(ierr);
7171         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
7172       }
7173       break;
7174     case DM_POLYTOPE_TRI_PRISM_TENSOR:
7175     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
7176       ierr = DMPlexGetCone(dm, c, &cone);CHKERRQ(ierr);
7177       ierr = DMPlexGetConeSize(dm, c, &coneSize);CHKERRQ(ierr);
7178       for (cp = 0; cp < coneSize; ++cp) {
7179         ierr = DMPlexGetCone(dm, cone[cp], &ccone);CHKERRQ(ierr);
7180         ierr = DMPlexGetConeSize(dm, cone[cp], &cconeSize);CHKERRQ(ierr);
7181         for (ccp = 0; ccp < cconeSize; ++ccp) {
7182           ierr = DMPlexGetCellType(dm, ccone[ccp], &cct);CHKERRQ(ierr);
7183           if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
7184             PetscInt p;
7185             for (p = 0; p < npt; ++p) if (ptpoints[p] == ccone[ccp]) break;
7186             if (p == npt) ptpoints[npt++] = ccone[ccp];
7187           }
7188         }
7189       }
7190       break;
7191     default: break;
7192   }
7193   for (pt = 0; pt < npt; ++pt) {
7194     ierr = DMPlexGetCone(dm, ptpoints[pt], &ptcone);CHKERRQ(ierr);
7195     if (ptcone[0] == ptcone[1]) ++(*unsplit);
7196   }
7197   PetscFunctionReturn(0);
7198 }
7199 
7200 /*@
7201   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
7202 
7203   Input Parameters:
7204 + dm - The DMPlex object
7205 - cellHeight - Normally 0
7206 
7207   Notes:
7208   This is a useful diagnostic when creating meshes programmatically.
7209   Currently applicable only to homogeneous simplex or tensor meshes.
7210 
7211   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
7212 
7213   Level: developer
7214 
7215 .seealso: DMCreate(), DMSetFromOptions()
7216 @*/
7217 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
7218 {
7219   DMPlexInterpolatedFlag interp;
7220   DMPolytopeType         ct;
7221   PetscInt               vStart, vEnd, cStart, cEnd, c;
7222   PetscErrorCode         ierr;
7223 
7224   PetscFunctionBegin;
7225   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7226   ierr = DMPlexIsInterpolated(dm, &interp);CHKERRQ(ierr);
7227   ierr = DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd);CHKERRQ(ierr);
7228   ierr = DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd);CHKERRQ(ierr);
7229   for (c = cStart; c < cEnd; ++c) {
7230     PetscInt *closure = NULL;
7231     PetscInt  coneSize, closureSize, cl, Nv = 0;
7232 
7233     ierr = DMPlexGetCellType(dm, c, &ct);CHKERRQ(ierr);
7234     if ((PetscInt) ct < 0) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %D has no cell type", c);
7235     if (ct == DM_POLYTOPE_UNKNOWN) continue;
7236     if (interp == DMPLEX_INTERPOLATED_FULL) {
7237       ierr = DMPlexGetConeSize(dm, c, &coneSize);CHKERRQ(ierr);
7238       if (coneSize != DMPolytopeTypeGetConeSize(ct)) SETERRQ3(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %D has cone size %D != %D", c, coneSize, DMPolytopeTypeGetConeSize(ct));
7239     }
7240     ierr = DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure);CHKERRQ(ierr);
7241     for (cl = 0; cl < closureSize*2; cl += 2) {
7242       const PetscInt p = closure[cl];
7243       if ((p >= vStart) && (p < vEnd)) ++Nv;
7244     }
7245     ierr = DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure);CHKERRQ(ierr);
7246     /* Special Case: Tensor faces with identified vertices */
7247     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
7248       PetscInt unsplit;
7249 
7250       ierr = DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit);CHKERRQ(ierr);
7251       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
7252     }
7253     if (Nv != DMPolytopeTypeGetNumVertices(ct)) SETERRQ3(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %D has %D vertices != %D", c, Nv, DMPolytopeTypeGetNumVertices(ct));
7254   }
7255   PetscFunctionReturn(0);
7256 }
7257 
7258 /*@
7259   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
7260 
7261   Not Collective
7262 
7263   Input Parameters:
7264 + dm - The DMPlex object
7265 - cellHeight - Normally 0
7266 
7267   Notes:
7268   This is a useful diagnostic when creating meshes programmatically.
7269   This routine is only relevant for meshes that are fully interpolated across all ranks.
7270   It will error out if a partially interpolated mesh is given on some rank.
7271   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
7272 
7273   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
7274 
7275   Level: developer
7276 
7277 .seealso: DMCreate(), DMPlexGetVTKCellHeight(), DMSetFromOptions()
7278 @*/
7279 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
7280 {
7281   PetscInt       dim, depth, vStart, vEnd, cStart, cEnd, c, h;
7282   PetscErrorCode ierr;
7283   DMPlexInterpolatedFlag interpEnum;
7284 
7285   PetscFunctionBegin;
7286   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7287   ierr = DMPlexIsInterpolated(dm, &interpEnum);CHKERRQ(ierr);
7288   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(0);
7289   if (interpEnum == DMPLEX_INTERPOLATED_PARTIAL) {
7290     PetscMPIInt	rank;
7291     MPI_Comm	comm;
7292 
7293     ierr = PetscObjectGetComm((PetscObject) dm, &comm);CHKERRQ(ierr);
7294     ierr = MPI_Comm_rank(comm, &rank);CHKERRQ(ierr);
7295     SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_SUP, "Mesh is only partially interpolated on rank %d, this is currently not supported", rank);
7296   }
7297 
7298   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
7299   ierr = DMPlexGetDepth(dm, &depth);CHKERRQ(ierr);
7300   ierr = DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd);CHKERRQ(ierr);
7301   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
7302     ierr = DMPlexGetHeightStratum(dm, h, &cStart, &cEnd);CHKERRQ(ierr);
7303     for (c = cStart; c < cEnd; ++c) {
7304       const PetscInt      *cone, *ornt, *faceSizes, *faces;
7305       const DMPolytopeType *faceTypes;
7306       DMPolytopeType        ct;
7307       PetscInt              numFaces, coneSize, f;
7308       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
7309 
7310       ierr = DMPlexGetCellType(dm, c, &ct);CHKERRQ(ierr);
7311       ierr = DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit);CHKERRQ(ierr);
7312       if (unsplit) continue;
7313       ierr = DMPlexGetConeSize(dm, c, &coneSize);CHKERRQ(ierr);
7314       ierr = DMPlexGetCone(dm, c, &cone);CHKERRQ(ierr);
7315       ierr = DMPlexGetConeOrientation(dm, c, &ornt);CHKERRQ(ierr);
7316       ierr = DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure);CHKERRQ(ierr);
7317       for (cl = 0; cl < closureSize*2; cl += 2) {
7318         const PetscInt p = closure[cl];
7319         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
7320       }
7321       ierr = DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces);CHKERRQ(ierr);
7322       if (coneSize != numFaces) SETERRQ3(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %D has %D faces but should have %D", c, coneSize, numFaces);
7323       for (f = 0; f < numFaces; ++f) {
7324         PetscInt *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
7325 
7326         ierr = DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure);CHKERRQ(ierr);
7327         for (cl = 0; cl < fclosureSize*2; cl += 2) {
7328           const PetscInt p = fclosure[cl];
7329           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
7330         }
7331         if (fnumCorners != faceSizes[f]) SETERRQ5(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %D (%D) of cell %D has %D vertices but should have %D", cone[f], f, c, fnumCorners, faceSizes[f]);
7332         for (v = 0; v < fnumCorners; ++v) {
7333           if (fclosure[v] != faces[fOff+v]) SETERRQ6(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %D (%d) of cell %D vertex %D, %D != %D", cone[f], f, c, v, fclosure[v], faces[fOff+v]);
7334         }
7335         ierr = DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure);CHKERRQ(ierr);
7336         fOff += faceSizes[f];
7337       }
7338       ierr = DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces);CHKERRQ(ierr);
7339       ierr = DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure);CHKERRQ(ierr);
7340     }
7341   }
7342   PetscFunctionReturn(0);
7343 }
7344 
7345 /*@
7346   DMPlexCheckGeometry - Check the geometry of mesh cells
7347 
7348   Input Parameter:
7349 . dm - The DMPlex object
7350 
7351   Notes:
7352   This is a useful diagnostic when creating meshes programmatically.
7353 
7354   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
7355 
7356   Level: developer
7357 
7358 .seealso: DMCreate(), DMSetFromOptions()
7359 @*/
7360 PetscErrorCode DMPlexCheckGeometry(DM dm)
7361 {
7362   PetscReal      detJ, J[9], refVol = 1.0;
7363   PetscReal      vol;
7364   PetscBool      periodic;
7365   PetscInt       dim, depth, d, cStart, cEnd, c;
7366   PetscErrorCode ierr;
7367 
7368   PetscFunctionBegin;
7369   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
7370   ierr = DMPlexGetDepth(dm, &depth);CHKERRQ(ierr);
7371   ierr = DMGetPeriodicity(dm, &periodic, NULL, NULL, NULL);CHKERRQ(ierr);
7372   for (d = 0; d < dim; ++d) refVol *= 2.0;
7373   ierr = DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd);CHKERRQ(ierr);
7374   for (c = cStart; c < cEnd; ++c) {
7375     DMPolytopeType ct;
7376     PetscInt       unsplit;
7377     PetscBool      ignoreZeroVol = PETSC_FALSE;
7378 
7379     ierr = DMPlexGetCellType(dm, c, &ct);CHKERRQ(ierr);
7380     switch (ct) {
7381       case DM_POLYTOPE_SEG_PRISM_TENSOR:
7382       case DM_POLYTOPE_TRI_PRISM_TENSOR:
7383       case DM_POLYTOPE_QUAD_PRISM_TENSOR:
7384         ignoreZeroVol = PETSC_TRUE; break;
7385       default: break;
7386     }
7387     switch (ct) {
7388       case DM_POLYTOPE_TRI_PRISM:
7389       case DM_POLYTOPE_TRI_PRISM_TENSOR:
7390       case DM_POLYTOPE_QUAD_PRISM_TENSOR:
7391         continue;
7392       default: break;
7393     }
7394     ierr = DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit);CHKERRQ(ierr);
7395     if (unsplit) continue;
7396     ierr = DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ);CHKERRQ(ierr);
7397     if (detJ < -PETSC_SMALL || (detJ <= 0.0 && !ignoreZeroVol)) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %D is inverted, |J| = %g", c, (double) detJ);
7398     ierr = PetscInfo2(dm, "Cell %D FEM Volume %g\n", c, (double) detJ*refVol);CHKERRQ(ierr);
7399     if (depth > 1 && !periodic) {
7400       ierr = DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL);CHKERRQ(ierr);
7401       if (vol < -PETSC_SMALL || (vol <= 0.0 && !ignoreZeroVol)) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %d is inverted, vol = %g", c, (double) vol);
7402       ierr = PetscInfo2(dm, "Cell %D FVM Volume %g\n", c, (double) vol);CHKERRQ(ierr);
7403     }
7404   }
7405   PetscFunctionReturn(0);
7406 }
7407 
7408 /*@
7409   DMPlexCheckPointSF - Check that several necessary conditions are met for the point SF of this plex.
7410 
7411   Input Parameters:
7412 . dm - The DMPlex object
7413 
7414   Notes:
7415   This is mainly intended for debugging/testing purposes.
7416   It currently checks only meshes with no partition overlapping.
7417 
7418   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
7419 
7420   Level: developer
7421 
7422 .seealso: DMGetPointSF(), DMSetFromOptions()
7423 @*/
7424 PetscErrorCode DMPlexCheckPointSF(DM dm)
7425 {
7426   PetscSF         pointSF;
7427   PetscInt        cellHeight, cStart, cEnd, l, nleaves, nroots, overlap;
7428   const PetscInt *locals, *rootdegree;
7429   PetscBool       distributed;
7430   PetscErrorCode  ierr;
7431 
7432   PetscFunctionBegin;
7433   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7434   ierr = DMGetPointSF(dm, &pointSF);CHKERRQ(ierr);
7435   ierr = DMPlexIsDistributed(dm, &distributed);CHKERRQ(ierr);
7436   if (!distributed) PetscFunctionReturn(0);
7437   ierr = DMPlexGetOverlap(dm, &overlap);CHKERRQ(ierr);
7438   if (overlap) {
7439     ierr = PetscPrintf(PetscObjectComm((PetscObject)dm), "Warning: DMPlexCheckPointSF() is currently not implemented for meshes with partition overlapping");
7440     PetscFunctionReturn(0);
7441   }
7442   if (!pointSF) SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "This DMPlex is distributed but does not have PointSF attached");
7443   ierr = PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, NULL);CHKERRQ(ierr);
7444   if (nroots < 0) SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "This DMPlex is distributed but its PointSF has no graph set");
7445   ierr = PetscSFComputeDegreeBegin(pointSF, &rootdegree);CHKERRQ(ierr);
7446   ierr = PetscSFComputeDegreeEnd(pointSF, &rootdegree);CHKERRQ(ierr);
7447 
7448   /* 1) check there are no faces in 2D, cells in 3D, in interface */
7449   ierr = DMPlexGetVTKCellHeight(dm, &cellHeight);CHKERRQ(ierr);
7450   ierr = DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd);CHKERRQ(ierr);
7451   for (l = 0; l < nleaves; ++l) {
7452     const PetscInt point = locals[l];
7453 
7454     if (point >= cStart && point < cEnd) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %D which is a cell", point);
7455   }
7456 
7457   /* 2) if some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
7458   for (l = 0; l < nleaves; ++l) {
7459     const PetscInt  point = locals[l];
7460     const PetscInt *cone;
7461     PetscInt        coneSize, c, idx;
7462 
7463     ierr = DMPlexGetConeSize(dm, point, &coneSize);CHKERRQ(ierr);
7464     ierr = DMPlexGetCone(dm, point, &cone);CHKERRQ(ierr);
7465     for (c = 0; c < coneSize; ++c) {
7466       if (!rootdegree[cone[c]]) {
7467         ierr = PetscFindInt(cone[c], nleaves, locals, &idx);CHKERRQ(ierr);
7468         if (idx < 0) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %D but not %D from its cone", point, cone[c]);
7469       }
7470     }
7471   }
7472   PetscFunctionReturn(0);
7473 }
7474 
7475 typedef struct cell_stats
7476 {
7477   PetscReal min, max, sum, squaresum;
7478   PetscInt  count;
7479 } cell_stats_t;
7480 
7481 static void MPIAPI cell_stats_reduce(void *a, void *b, int * len, MPI_Datatype *datatype)
7482 {
7483   PetscInt i, N = *len;
7484 
7485   for (i = 0; i < N; i++) {
7486     cell_stats_t *A = (cell_stats_t *) a;
7487     cell_stats_t *B = (cell_stats_t *) b;
7488 
7489     B->min = PetscMin(A->min,B->min);
7490     B->max = PetscMax(A->max,B->max);
7491     B->sum += A->sum;
7492     B->squaresum += A->squaresum;
7493     B->count += A->count;
7494   }
7495 }
7496 
7497 /*@
7498   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
7499 
7500   Collective on dm
7501 
7502   Input Parameters:
7503 + dm        - The DMPlex object
7504 . output    - If true, statistics will be displayed on stdout
7505 - condLimit - Display all cells above this condition number, or PETSC_DETERMINE for no cell output
7506 
7507   Notes:
7508   This is mainly intended for debugging/testing purposes.
7509 
7510   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
7511 
7512   Level: developer
7513 
7514 .seealso: DMSetFromOptions()
7515 @*/
7516 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
7517 {
7518   DM             dmCoarse;
7519   cell_stats_t   stats, globalStats;
7520   MPI_Comm       comm = PetscObjectComm((PetscObject)dm);
7521   PetscReal      *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
7522   PetscReal      limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
7523   PetscInt       cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
7524   PetscMPIInt    rank,size;
7525   PetscErrorCode ierr;
7526 
7527   PetscFunctionBegin;
7528   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7529   stats.min   = PETSC_MAX_REAL;
7530   stats.max   = PETSC_MIN_REAL;
7531   stats.sum   = stats.squaresum = 0.;
7532   stats.count = 0;
7533 
7534   ierr = MPI_Comm_size(comm, &size);CHKERRQ(ierr);
7535   ierr = MPI_Comm_rank(comm, &rank);CHKERRQ(ierr);
7536   ierr = DMGetCoordinateDim(dm,&cdim);CHKERRQ(ierr);
7537   ierr = PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ);CHKERRQ(ierr);
7538   ierr = DMPlexGetSimplexOrBoxCells(dm,0,&cStart,&cEnd);CHKERRQ(ierr);
7539   ierr = DMPlexGetDepthStratum(dm,1,&eStart,&eEnd);CHKERRQ(ierr);
7540   for (c = cStart; c < cEnd; c++) {
7541     PetscInt  i;
7542     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
7543 
7544     ierr = DMPlexComputeCellGeometryAffineFEM(dm,c,NULL,J,invJ,&detJ);CHKERRQ(ierr);
7545     if (detJ < 0.0) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %D is inverted", c);
7546     for (i = 0; i < PetscSqr(cdim); ++i) {
7547       frobJ    += J[i] * J[i];
7548       frobInvJ += invJ[i] * invJ[i];
7549     }
7550     cond2 = frobJ * frobInvJ;
7551     cond  = PetscSqrtReal(cond2);
7552 
7553     stats.min        = PetscMin(stats.min,cond);
7554     stats.max        = PetscMax(stats.max,cond);
7555     stats.sum       += cond;
7556     stats.squaresum += cond2;
7557     stats.count++;
7558     if (output && cond > limit) {
7559       PetscSection coordSection;
7560       Vec          coordsLocal;
7561       PetscScalar *coords = NULL;
7562       PetscInt     Nv, d, clSize, cl, *closure = NULL;
7563 
7564       ierr = DMGetCoordinatesLocal(dm, &coordsLocal);CHKERRQ(ierr);
7565       ierr = DMGetCoordinateSection(dm, &coordSection);CHKERRQ(ierr);
7566       ierr = DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords);CHKERRQ(ierr);
7567       ierr = PetscSynchronizedPrintf(comm, "[%d] Cell %D cond %g\n", rank, c, (double) cond);CHKERRQ(ierr);
7568       for (i = 0; i < Nv/cdim; ++i) {
7569         ierr = PetscSynchronizedPrintf(comm, "  Vertex %D: (", i);CHKERRQ(ierr);
7570         for (d = 0; d < cdim; ++d) {
7571           if (d > 0) {ierr = PetscSynchronizedPrintf(comm, ", ");CHKERRQ(ierr);}
7572           ierr = PetscSynchronizedPrintf(comm, "%g", (double) PetscRealPart(coords[i*cdim+d]));CHKERRQ(ierr);
7573         }
7574         ierr = PetscSynchronizedPrintf(comm, ")\n");CHKERRQ(ierr);
7575       }
7576       ierr = DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure);CHKERRQ(ierr);
7577       for (cl = 0; cl < clSize*2; cl += 2) {
7578         const PetscInt edge = closure[cl];
7579 
7580         if ((edge >= eStart) && (edge < eEnd)) {
7581           PetscReal len;
7582 
7583           ierr = DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL);CHKERRQ(ierr);
7584           ierr = PetscSynchronizedPrintf(comm, "  Edge %D: length %g\n", edge, (double) len);CHKERRQ(ierr);
7585         }
7586       }
7587       ierr = DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure);CHKERRQ(ierr);
7588       ierr = DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords);CHKERRQ(ierr);
7589     }
7590   }
7591   if (output) {ierr = PetscSynchronizedFlush(comm, NULL);CHKERRQ(ierr);}
7592 
7593   if (size > 1) {
7594     PetscMPIInt   blockLengths[2] = {4,1};
7595     MPI_Aint      blockOffsets[2] = {offsetof(cell_stats_t,min),offsetof(cell_stats_t,count)};
7596     MPI_Datatype  blockTypes[2]   = {MPIU_REAL,MPIU_INT}, statType;
7597     MPI_Op        statReduce;
7598 
7599     ierr = MPI_Type_create_struct(2,blockLengths,blockOffsets,blockTypes,&statType);CHKERRQ(ierr);
7600     ierr = MPI_Type_commit(&statType);CHKERRQ(ierr);
7601     ierr = MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce);CHKERRQ(ierr);
7602     ierr = MPI_Reduce(&stats,&globalStats,1,statType,statReduce,0,comm);CHKERRQ(ierr);
7603     ierr = MPI_Op_free(&statReduce);CHKERRQ(ierr);
7604     ierr = MPI_Type_free(&statType);CHKERRQ(ierr);
7605   } else {
7606     ierr = PetscArraycpy(&globalStats,&stats,1);CHKERRQ(ierr);
7607   }
7608   if (!rank) {
7609     count = globalStats.count;
7610     min   = globalStats.min;
7611     max   = globalStats.max;
7612     mean  = globalStats.sum / globalStats.count;
7613     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1),0)) : 0.0;
7614   }
7615 
7616   if (output) {
7617     ierr = PetscPrintf(comm,"Mesh with %D cells, shape condition numbers: min = %g, max = %g, mean = %g, stddev = %g\n", count, (double) min, (double) max, (double) mean, (double) stdev);CHKERRQ(ierr);
7618   }
7619   ierr = PetscFree2(J,invJ);CHKERRQ(ierr);
7620 
7621   ierr = DMGetCoarseDM(dm,&dmCoarse);CHKERRQ(ierr);
7622   if (dmCoarse) {
7623     PetscBool isplex;
7624 
7625     ierr = PetscObjectTypeCompare((PetscObject)dmCoarse,DMPLEX,&isplex);CHKERRQ(ierr);
7626     if (isplex) {
7627       ierr = DMPlexCheckCellShape(dmCoarse,output,condLimit);CHKERRQ(ierr);
7628     }
7629   }
7630   PetscFunctionReturn(0);
7631 }
7632 
7633 /* Pointwise interpolation
7634      Just code FEM for now
7635      u^f = I u^c
7636      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
7637      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
7638      I_{ij} = psi^f_i phi^c_j
7639 */
7640 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
7641 {
7642   PetscSection   gsc, gsf;
7643   PetscInt       m, n;
7644   void          *ctx;
7645   DM             cdm;
7646   PetscBool      regular, ismatis;
7647   PetscErrorCode ierr;
7648 
7649   PetscFunctionBegin;
7650   ierr = DMGetGlobalSection(dmFine, &gsf);CHKERRQ(ierr);
7651   ierr = PetscSectionGetConstrainedStorageSize(gsf, &m);CHKERRQ(ierr);
7652   ierr = DMGetGlobalSection(dmCoarse, &gsc);CHKERRQ(ierr);
7653   ierr = PetscSectionGetConstrainedStorageSize(gsc, &n);CHKERRQ(ierr);
7654 
7655   ierr = PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis);CHKERRQ(ierr);
7656   ierr = MatCreate(PetscObjectComm((PetscObject) dmCoarse), interpolation);CHKERRQ(ierr);
7657   ierr = MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE);CHKERRQ(ierr);
7658   ierr = MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype);CHKERRQ(ierr);
7659   ierr = DMGetApplicationContext(dmFine, &ctx);CHKERRQ(ierr);
7660 
7661   ierr = DMGetCoarseDM(dmFine, &cdm);CHKERRQ(ierr);
7662   ierr = DMPlexGetRegularRefinement(dmFine, &regular);CHKERRQ(ierr);
7663   if (regular && cdm == dmCoarse) {ierr = DMPlexComputeInterpolatorNested(dmCoarse, dmFine, *interpolation, ctx);CHKERRQ(ierr);}
7664   else                            {ierr = DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx);CHKERRQ(ierr);}
7665   ierr = MatViewFromOptions(*interpolation, NULL, "-interp_mat_view");CHKERRQ(ierr);
7666   if (scaling) {
7667     /* Use naive scaling */
7668     ierr = DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling);CHKERRQ(ierr);
7669   }
7670   PetscFunctionReturn(0);
7671 }
7672 
7673 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
7674 {
7675   PetscErrorCode ierr;
7676   VecScatter     ctx;
7677 
7678   PetscFunctionBegin;
7679   ierr = DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL);CHKERRQ(ierr);
7680   ierr = MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat);CHKERRQ(ierr);
7681   ierr = VecScatterDestroy(&ctx);CHKERRQ(ierr);
7682   PetscFunctionReturn(0);
7683 }
7684 
7685 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
7686 {
7687   PetscSection   gsc, gsf;
7688   PetscInt       m, n;
7689   void          *ctx;
7690   DM             cdm;
7691   PetscBool      regular;
7692   PetscErrorCode ierr;
7693 
7694   PetscFunctionBegin;
7695   ierr = DMGetGlobalSection(dmFine, &gsf);CHKERRQ(ierr);
7696   ierr = PetscSectionGetConstrainedStorageSize(gsf, &m);CHKERRQ(ierr);
7697   ierr = DMGetGlobalSection(dmCoarse, &gsc);CHKERRQ(ierr);
7698   ierr = PetscSectionGetConstrainedStorageSize(gsc, &n);CHKERRQ(ierr);
7699 
7700   ierr = MatCreate(PetscObjectComm((PetscObject) dmCoarse), mass);CHKERRQ(ierr);
7701   ierr = MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE);CHKERRQ(ierr);
7702   ierr = MatSetType(*mass, dmCoarse->mattype);CHKERRQ(ierr);
7703   ierr = DMGetApplicationContext(dmFine, &ctx);CHKERRQ(ierr);
7704 
7705   ierr = DMGetCoarseDM(dmFine, &cdm);CHKERRQ(ierr);
7706   ierr = DMPlexGetRegularRefinement(dmFine, &regular);CHKERRQ(ierr);
7707   if (regular && cdm == dmCoarse) {ierr = DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx);CHKERRQ(ierr);}
7708   else                            {ierr = DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx);CHKERRQ(ierr);}
7709   ierr = MatViewFromOptions(*mass, NULL, "-mass_mat_view");CHKERRQ(ierr);
7710   PetscFunctionReturn(0);
7711 }
7712 
7713 /*@
7714   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
7715 
7716   Input Parameter:
7717 . dm - The DMPlex object
7718 
7719   Output Parameter:
7720 . regular - The flag
7721 
7722   Level: intermediate
7723 
7724 .seealso: DMPlexSetRegularRefinement()
7725 @*/
7726 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
7727 {
7728   PetscFunctionBegin;
7729   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7730   PetscValidPointer(regular, 2);
7731   *regular = ((DM_Plex *) dm->data)->regularRefinement;
7732   PetscFunctionReturn(0);
7733 }
7734 
7735 /*@
7736   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
7737 
7738   Input Parameters:
7739 + dm - The DMPlex object
7740 - regular - The flag
7741 
7742   Level: intermediate
7743 
7744 .seealso: DMPlexGetRegularRefinement()
7745 @*/
7746 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
7747 {
7748   PetscFunctionBegin;
7749   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7750   ((DM_Plex *) dm->data)->regularRefinement = regular;
7751   PetscFunctionReturn(0);
7752 }
7753 
7754 /*@
7755   DMPlexGetCellRefinerType - Get the strategy for refining a cell
7756 
7757   Input Parameter:
7758 . dm - The DMPlex object
7759 
7760   Output Parameter:
7761 . cr - The strategy number
7762 
7763   Level: intermediate
7764 
7765 .seealso: DMPlexSetCellRefinerType(), DMPlexSetRegularRefinement()
7766 @*/
7767 PetscErrorCode DMPlexGetCellRefinerType(DM dm, DMPlexCellRefinerType *cr)
7768 {
7769   PetscFunctionBegin;
7770   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7771   PetscValidPointer(cr, 2);
7772   *cr = ((DM_Plex *) dm->data)->cellRefiner;
7773   PetscFunctionReturn(0);
7774 }
7775 
7776 /*@
7777   DMPlexSetCellRefinerType - Set the strategy for refining a cell
7778 
7779   Input Parameters:
7780 + dm - The DMPlex object
7781 - cr - The strategy number
7782 
7783   Level: intermediate
7784 
7785 .seealso: DMPlexGetCellRefinerType(), DMPlexGetRegularRefinement()
7786 @*/
7787 PetscErrorCode DMPlexSetCellRefinerType(DM dm, DMPlexCellRefinerType cr)
7788 {
7789   PetscFunctionBegin;
7790   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7791   ((DM_Plex *) dm->data)->cellRefiner = cr;
7792   PetscFunctionReturn(0);
7793 }
7794 
7795 /* anchors */
7796 /*@
7797   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
7798   call DMPlexGetAnchors() directly: if there are anchors, then DMPlexGetAnchors() is called during DMGetConstraints().
7799 
7800   not collective
7801 
7802   Input Parameters:
7803 . dm - The DMPlex object
7804 
7805   Output Parameters:
7806 + anchorSection - If not NULL, set to the section describing which points anchor the constrained points.
7807 - anchorIS - If not NULL, set to the list of anchors indexed by anchorSection
7808 
7809 
7810   Level: intermediate
7811 
7812 .seealso: DMPlexSetAnchors(), DMGetConstraints(), DMSetConstraints()
7813 @*/
7814 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
7815 {
7816   DM_Plex *plex = (DM_Plex *)dm->data;
7817   PetscErrorCode ierr;
7818 
7819   PetscFunctionBegin;
7820   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7821   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) {ierr = (*plex->createanchors)(dm);CHKERRQ(ierr);}
7822   if (anchorSection) *anchorSection = plex->anchorSection;
7823   if (anchorIS) *anchorIS = plex->anchorIS;
7824   PetscFunctionReturn(0);
7825 }
7826 
7827 /*@
7828   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.  Unlike boundary conditions,
7829   when a point's degrees of freedom in a section are constrained to an outside value, the anchor constraints set a
7830   point's degrees of freedom to be a linear combination of other points' degrees of freedom.
7831 
7832   After specifying the layout of constraints with DMPlexSetAnchors(), one specifies the constraints by calling
7833   DMGetConstraints() and filling in the entries in the constraint matrix.
7834 
7835   collective on dm
7836 
7837   Input Parameters:
7838 + dm - The DMPlex object
7839 . 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).
7840 - anchorIS - The list of all anchor points.  Must have a local communicator (PETSC_COMM_SELF or derivative).
7841 
7842   The reference counts of anchorSection and anchorIS are incremented.
7843 
7844   Level: intermediate
7845 
7846 .seealso: DMPlexGetAnchors(), DMGetConstraints(), DMSetConstraints()
7847 @*/
7848 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
7849 {
7850   DM_Plex        *plex = (DM_Plex *)dm->data;
7851   PetscMPIInt    result;
7852   PetscErrorCode ierr;
7853 
7854   PetscFunctionBegin;
7855   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7856   if (anchorSection) {
7857     PetscValidHeaderSpecific(anchorSection,PETSC_SECTION_CLASSID,2);
7858     ierr = MPI_Comm_compare(PETSC_COMM_SELF,PetscObjectComm((PetscObject)anchorSection),&result);CHKERRQ(ierr);
7859     if (result != MPI_CONGRUENT && result != MPI_IDENT) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_NOTSAMECOMM,"anchor section must have local communicator");
7860   }
7861   if (anchorIS) {
7862     PetscValidHeaderSpecific(anchorIS,IS_CLASSID,3);
7863     ierr = MPI_Comm_compare(PETSC_COMM_SELF,PetscObjectComm((PetscObject)anchorIS),&result);CHKERRQ(ierr);
7864     if (result != MPI_CONGRUENT && result != MPI_IDENT) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_NOTSAMECOMM,"anchor IS must have local communicator");
7865   }
7866 
7867   ierr = PetscObjectReference((PetscObject)anchorSection);CHKERRQ(ierr);
7868   ierr = PetscSectionDestroy(&plex->anchorSection);CHKERRQ(ierr);
7869   plex->anchorSection = anchorSection;
7870 
7871   ierr = PetscObjectReference((PetscObject)anchorIS);CHKERRQ(ierr);
7872   ierr = ISDestroy(&plex->anchorIS);CHKERRQ(ierr);
7873   plex->anchorIS = anchorIS;
7874 
7875   if (PetscDefined(USE_DEBUG) && anchorIS && anchorSection) {
7876     PetscInt size, a, pStart, pEnd;
7877     const PetscInt *anchors;
7878 
7879     ierr = PetscSectionGetChart(anchorSection,&pStart,&pEnd);CHKERRQ(ierr);
7880     ierr = ISGetLocalSize(anchorIS,&size);CHKERRQ(ierr);
7881     ierr = ISGetIndices(anchorIS,&anchors);CHKERRQ(ierr);
7882     for (a = 0; a < size; a++) {
7883       PetscInt p;
7884 
7885       p = anchors[a];
7886       if (p >= pStart && p < pEnd) {
7887         PetscInt dof;
7888 
7889         ierr = PetscSectionGetDof(anchorSection,p,&dof);CHKERRQ(ierr);
7890         if (dof) {
7891           PetscErrorCode ierr2;
7892 
7893           ierr2 = ISRestoreIndices(anchorIS,&anchors);CHKERRQ(ierr2);
7894           SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Point %D cannot be constrained and an anchor",p);
7895         }
7896       }
7897     }
7898     ierr = ISRestoreIndices(anchorIS,&anchors);CHKERRQ(ierr);
7899   }
7900   /* reset the generic constraints */
7901   ierr = DMSetDefaultConstraints(dm,NULL,NULL);CHKERRQ(ierr);
7902   PetscFunctionReturn(0);
7903 }
7904 
7905 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
7906 {
7907   PetscSection anchorSection;
7908   PetscInt pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
7909   PetscErrorCode ierr;
7910 
7911   PetscFunctionBegin;
7912   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7913   ierr = DMPlexGetAnchors(dm,&anchorSection,NULL);CHKERRQ(ierr);
7914   ierr = PetscSectionCreate(PETSC_COMM_SELF,cSec);CHKERRQ(ierr);
7915   ierr = PetscSectionGetNumFields(section,&numFields);CHKERRQ(ierr);
7916   if (numFields) {
7917     PetscInt f;
7918     ierr = PetscSectionSetNumFields(*cSec,numFields);CHKERRQ(ierr);
7919 
7920     for (f = 0; f < numFields; f++) {
7921       PetscInt numComp;
7922 
7923       ierr = PetscSectionGetFieldComponents(section,f,&numComp);CHKERRQ(ierr);
7924       ierr = PetscSectionSetFieldComponents(*cSec,f,numComp);CHKERRQ(ierr);
7925     }
7926   }
7927   ierr = PetscSectionGetChart(anchorSection,&pStart,&pEnd);CHKERRQ(ierr);
7928   ierr = PetscSectionGetChart(section,&sStart,&sEnd);CHKERRQ(ierr);
7929   pStart = PetscMax(pStart,sStart);
7930   pEnd   = PetscMin(pEnd,sEnd);
7931   pEnd   = PetscMax(pStart,pEnd);
7932   ierr = PetscSectionSetChart(*cSec,pStart,pEnd);CHKERRQ(ierr);
7933   for (p = pStart; p < pEnd; p++) {
7934     ierr = PetscSectionGetDof(anchorSection,p,&dof);CHKERRQ(ierr);
7935     if (dof) {
7936       ierr = PetscSectionGetDof(section,p,&dof);CHKERRQ(ierr);
7937       ierr = PetscSectionSetDof(*cSec,p,dof);CHKERRQ(ierr);
7938       for (f = 0; f < numFields; f++) {
7939         ierr = PetscSectionGetFieldDof(section,p,f,&dof);CHKERRQ(ierr);
7940         ierr = PetscSectionSetFieldDof(*cSec,p,f,dof);CHKERRQ(ierr);
7941       }
7942     }
7943   }
7944   ierr = PetscSectionSetUp(*cSec);CHKERRQ(ierr);
7945   PetscFunctionReturn(0);
7946 }
7947 
7948 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
7949 {
7950   PetscSection aSec;
7951   PetscInt pStart, pEnd, p, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
7952   const PetscInt *anchors;
7953   PetscInt numFields, f;
7954   IS aIS;
7955   PetscErrorCode ierr;
7956 
7957   PetscFunctionBegin;
7958   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7959   ierr = PetscSectionGetStorageSize(cSec, &m);CHKERRQ(ierr);
7960   ierr = PetscSectionGetStorageSize(section, &n);CHKERRQ(ierr);
7961   ierr = MatCreate(PETSC_COMM_SELF,cMat);CHKERRQ(ierr);
7962   ierr = MatSetSizes(*cMat,m,n,m,n);CHKERRQ(ierr);
7963   ierr = MatSetType(*cMat,MATSEQAIJ);CHKERRQ(ierr);
7964   ierr = DMPlexGetAnchors(dm,&aSec,&aIS);CHKERRQ(ierr);
7965   ierr = ISGetIndices(aIS,&anchors);CHKERRQ(ierr);
7966   /* cSec will be a subset of aSec and section */
7967   ierr = PetscSectionGetChart(cSec,&pStart,&pEnd);CHKERRQ(ierr);
7968   ierr = PetscMalloc1(m+1,&i);CHKERRQ(ierr);
7969   i[0] = 0;
7970   ierr = PetscSectionGetNumFields(section,&numFields);CHKERRQ(ierr);
7971   for (p = pStart; p < pEnd; p++) {
7972     PetscInt rDof, rOff, r;
7973 
7974     ierr = PetscSectionGetDof(aSec,p,&rDof);CHKERRQ(ierr);
7975     if (!rDof) continue;
7976     ierr = PetscSectionGetOffset(aSec,p,&rOff);CHKERRQ(ierr);
7977     if (numFields) {
7978       for (f = 0; f < numFields; f++) {
7979         annz = 0;
7980         for (r = 0; r < rDof; r++) {
7981           a = anchors[rOff + r];
7982           ierr = PetscSectionGetFieldDof(section,a,f,&aDof);CHKERRQ(ierr);
7983           annz += aDof;
7984         }
7985         ierr = PetscSectionGetFieldDof(cSec,p,f,&dof);CHKERRQ(ierr);
7986         ierr = PetscSectionGetFieldOffset(cSec,p,f,&off);CHKERRQ(ierr);
7987         for (q = 0; q < dof; q++) {
7988           i[off + q + 1] = i[off + q] + annz;
7989         }
7990       }
7991     }
7992     else {
7993       annz = 0;
7994       for (q = 0; q < dof; q++) {
7995         a = anchors[off + q];
7996         ierr = PetscSectionGetDof(section,a,&aDof);CHKERRQ(ierr);
7997         annz += aDof;
7998       }
7999       ierr = PetscSectionGetDof(cSec,p,&dof);CHKERRQ(ierr);
8000       ierr = PetscSectionGetOffset(cSec,p,&off);CHKERRQ(ierr);
8001       for (q = 0; q < dof; q++) {
8002         i[off + q + 1] = i[off + q] + annz;
8003       }
8004     }
8005   }
8006   nnz = i[m];
8007   ierr = PetscMalloc1(nnz,&j);CHKERRQ(ierr);
8008   offset = 0;
8009   for (p = pStart; p < pEnd; p++) {
8010     if (numFields) {
8011       for (f = 0; f < numFields; f++) {
8012         ierr = PetscSectionGetFieldDof(cSec,p,f,&dof);CHKERRQ(ierr);
8013         for (q = 0; q < dof; q++) {
8014           PetscInt rDof, rOff, r;
8015           ierr = PetscSectionGetDof(aSec,p,&rDof);CHKERRQ(ierr);
8016           ierr = PetscSectionGetOffset(aSec,p,&rOff);CHKERRQ(ierr);
8017           for (r = 0; r < rDof; r++) {
8018             PetscInt s;
8019 
8020             a = anchors[rOff + r];
8021             ierr = PetscSectionGetFieldDof(section,a,f,&aDof);CHKERRQ(ierr);
8022             ierr = PetscSectionGetFieldOffset(section,a,f,&aOff);CHKERRQ(ierr);
8023             for (s = 0; s < aDof; s++) {
8024               j[offset++] = aOff + s;
8025             }
8026           }
8027         }
8028       }
8029     }
8030     else {
8031       ierr = PetscSectionGetDof(cSec,p,&dof);CHKERRQ(ierr);
8032       for (q = 0; q < dof; q++) {
8033         PetscInt rDof, rOff, r;
8034         ierr = PetscSectionGetDof(aSec,p,&rDof);CHKERRQ(ierr);
8035         ierr = PetscSectionGetOffset(aSec,p,&rOff);CHKERRQ(ierr);
8036         for (r = 0; r < rDof; r++) {
8037           PetscInt s;
8038 
8039           a = anchors[rOff + r];
8040           ierr = PetscSectionGetDof(section,a,&aDof);CHKERRQ(ierr);
8041           ierr = PetscSectionGetOffset(section,a,&aOff);CHKERRQ(ierr);
8042           for (s = 0; s < aDof; s++) {
8043             j[offset++] = aOff + s;
8044           }
8045         }
8046       }
8047     }
8048   }
8049   ierr = MatSeqAIJSetPreallocationCSR(*cMat,i,j,NULL);CHKERRQ(ierr);
8050   ierr = PetscFree(i);CHKERRQ(ierr);
8051   ierr = PetscFree(j);CHKERRQ(ierr);
8052   ierr = ISRestoreIndices(aIS,&anchors);CHKERRQ(ierr);
8053   PetscFunctionReturn(0);
8054 }
8055 
8056 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
8057 {
8058   DM_Plex        *plex = (DM_Plex *)dm->data;
8059   PetscSection   anchorSection, section, cSec;
8060   Mat            cMat;
8061   PetscErrorCode ierr;
8062 
8063   PetscFunctionBegin;
8064   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8065   ierr = DMPlexGetAnchors(dm,&anchorSection,NULL);CHKERRQ(ierr);
8066   if (anchorSection) {
8067     PetscInt Nf;
8068 
8069     ierr = DMGetLocalSection(dm,&section);CHKERRQ(ierr);
8070     ierr = DMPlexCreateConstraintSection_Anchors(dm,section,&cSec);CHKERRQ(ierr);
8071     ierr = DMPlexCreateConstraintMatrix_Anchors(dm,section,cSec,&cMat);CHKERRQ(ierr);
8072     ierr = DMGetNumFields(dm,&Nf);CHKERRQ(ierr);
8073     if (Nf && plex->computeanchormatrix) {ierr = (*plex->computeanchormatrix)(dm,section,cSec,cMat);CHKERRQ(ierr);}
8074     ierr = DMSetDefaultConstraints(dm,cSec,cMat);CHKERRQ(ierr);
8075     ierr = PetscSectionDestroy(&cSec);CHKERRQ(ierr);
8076     ierr = MatDestroy(&cMat);CHKERRQ(ierr);
8077   }
8078   PetscFunctionReturn(0);
8079 }
8080 
8081 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
8082 {
8083   IS             subis;
8084   PetscSection   section, subsection;
8085   PetscErrorCode ierr;
8086 
8087   PetscFunctionBegin;
8088   ierr = DMGetLocalSection(dm, &section);CHKERRQ(ierr);
8089   if (!section) SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
8090   if (!subdm)   SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
8091   /* Create subdomain */
8092   ierr = DMPlexFilter(dm, label, value, subdm);CHKERRQ(ierr);
8093   /* Create submodel */
8094   ierr = DMPlexGetSubpointIS(*subdm, &subis);CHKERRQ(ierr);
8095   ierr = PetscSectionCreateSubmeshSection(section, subis, &subsection);CHKERRQ(ierr);
8096   ierr = DMSetLocalSection(*subdm, subsection);CHKERRQ(ierr);
8097   ierr = PetscSectionDestroy(&subsection);CHKERRQ(ierr);
8098   ierr = DMCopyDisc(dm, *subdm);CHKERRQ(ierr);
8099   /* Create map from submodel to global model */
8100   if (is) {
8101     PetscSection    sectionGlobal, subsectionGlobal;
8102     IS              spIS;
8103     const PetscInt *spmap;
8104     PetscInt       *subIndices;
8105     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
8106     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
8107 
8108     ierr = DMPlexGetSubpointIS(*subdm, &spIS);CHKERRQ(ierr);
8109     ierr = ISGetIndices(spIS, &spmap);CHKERRQ(ierr);
8110     ierr = PetscSectionGetNumFields(section, &Nf);CHKERRQ(ierr);
8111     ierr = DMGetGlobalSection(dm, &sectionGlobal);CHKERRQ(ierr);
8112     ierr = DMGetGlobalSection(*subdm, &subsectionGlobal);CHKERRQ(ierr);
8113     ierr = PetscSectionGetChart(subsection, &pStart, &pEnd);CHKERRQ(ierr);
8114     for (p = pStart; p < pEnd; ++p) {
8115       PetscInt gdof, pSubSize  = 0;
8116 
8117       ierr = PetscSectionGetDof(sectionGlobal, p, &gdof);CHKERRQ(ierr);
8118       if (gdof > 0) {
8119         for (f = 0; f < Nf; ++f) {
8120           PetscInt fdof, fcdof;
8121 
8122           ierr     = PetscSectionGetFieldDof(subsection, p, f, &fdof);CHKERRQ(ierr);
8123           ierr     = PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof);CHKERRQ(ierr);
8124           pSubSize += fdof-fcdof;
8125         }
8126         subSize += pSubSize;
8127         if (pSubSize) {
8128           if (bs < 0) {
8129             bs = pSubSize;
8130           } else if (bs != pSubSize) {
8131             /* Layout does not admit a pointwise block size */
8132             bs = 1;
8133           }
8134         }
8135       }
8136     }
8137     /* Must have same blocksize on all procs (some might have no points) */
8138     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs; bsLocal[1] = bs;
8139     ierr = PetscGlobalMinMaxInt(PetscObjectComm((PetscObject) dm), bsLocal, bsMinMax);CHKERRQ(ierr);
8140     if (bsMinMax[0] != bsMinMax[1]) {bs = 1;}
8141     else                            {bs = bsMinMax[0];}
8142     ierr = PetscMalloc1(subSize, &subIndices);CHKERRQ(ierr);
8143     for (p = pStart; p < pEnd; ++p) {
8144       PetscInt gdof, goff;
8145 
8146       ierr = PetscSectionGetDof(subsectionGlobal, p, &gdof);CHKERRQ(ierr);
8147       if (gdof > 0) {
8148         const PetscInt point = spmap[p];
8149 
8150         ierr = PetscSectionGetOffset(sectionGlobal, point, &goff);CHKERRQ(ierr);
8151         for (f = 0; f < Nf; ++f) {
8152           PetscInt fdof, fcdof, fc, f2, poff = 0;
8153 
8154           /* Can get rid of this loop by storing field information in the global section */
8155           for (f2 = 0; f2 < f; ++f2) {
8156             ierr  = PetscSectionGetFieldDof(section, p, f2, &fdof);CHKERRQ(ierr);
8157             ierr  = PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof);CHKERRQ(ierr);
8158             poff += fdof-fcdof;
8159           }
8160           ierr = PetscSectionGetFieldDof(section, p, f, &fdof);CHKERRQ(ierr);
8161           ierr = PetscSectionGetFieldConstraintDof(section, p, f, &fcdof);CHKERRQ(ierr);
8162           for (fc = 0; fc < fdof-fcdof; ++fc, ++subOff) {
8163             subIndices[subOff] = goff+poff+fc;
8164           }
8165         }
8166       }
8167     }
8168     ierr = ISRestoreIndices(spIS, &spmap);CHKERRQ(ierr);
8169     ierr = ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is);CHKERRQ(ierr);
8170     if (bs > 1) {
8171       /* We need to check that the block size does not come from non-contiguous fields */
8172       PetscInt i, j, set = 1;
8173       for (i = 0; i < subSize; i += bs) {
8174         for (j = 0; j < bs; ++j) {
8175           if (subIndices[i+j] != subIndices[i]+j) {set = 0; break;}
8176         }
8177       }
8178       if (set) {ierr = ISSetBlockSize(*is, bs);CHKERRQ(ierr);}
8179     }
8180     /* Attach nullspace */
8181     for (f = 0; f < Nf; ++f) {
8182       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
8183       if ((*subdm)->nullspaceConstructors[f]) break;
8184     }
8185     if (f < Nf) {
8186       MatNullSpace nullSpace;
8187 
8188       ierr = (*(*subdm)->nullspaceConstructors[f])(*subdm, f, &nullSpace);CHKERRQ(ierr);
8189       ierr = PetscObjectCompose((PetscObject) *is, "nullspace", (PetscObject) nullSpace);CHKERRQ(ierr);
8190       ierr = MatNullSpaceDestroy(&nullSpace);CHKERRQ(ierr);
8191     }
8192   }
8193   PetscFunctionReturn(0);
8194 }
8195 
8196 /*@
8197   DMPlexMonitorThroughput - Report the cell throughput of FE integration
8198 
8199   Input Parameter:
8200 - dm - The DM
8201 
8202   Level: developer
8203 
8204   Options Database Keys:
8205 . -dm_plex_monitor_throughput - Activate the monitor
8206 
8207 .seealso: DMSetFromOptions(), DMPlexCreate()
8208 @*/
8209 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
8210 {
8211 #if defined(PETSC_USE_LOG)
8212   PetscStageLog      stageLog;
8213   PetscLogEvent      event;
8214   PetscLogStage      stage;
8215   PetscEventPerfInfo eventInfo;
8216   PetscReal          cellRate, flopRate;
8217   PetscInt           cStart, cEnd, Nf, N;
8218   const char        *name;
8219   PetscErrorCode     ierr;
8220 #endif
8221 
8222   PetscFunctionBegin;
8223   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8224 #if defined(PETSC_USE_LOG)
8225   ierr = PetscObjectGetName((PetscObject) dm, &name);CHKERRQ(ierr);
8226   ierr = DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd);CHKERRQ(ierr);
8227   ierr = DMGetNumFields(dm, &Nf);CHKERRQ(ierr);
8228   ierr = PetscLogGetStageLog(&stageLog);CHKERRQ(ierr);
8229   ierr = PetscStageLogGetCurrent(stageLog, &stage);CHKERRQ(ierr);
8230   ierr = PetscLogEventGetId("DMPlexResidualFE", &event);CHKERRQ(ierr);
8231   ierr = PetscLogEventGetPerfInfo(stage, event, &eventInfo);CHKERRQ(ierr);
8232   N        = (cEnd - cStart)*Nf*eventInfo.count;
8233   flopRate = eventInfo.flops/eventInfo.time;
8234   cellRate = N/eventInfo.time;
8235   ierr = PetscPrintf(PetscObjectComm((PetscObject) dm), "DM (%s) FE Residual Integration: %D 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));CHKERRQ(ierr);
8236 #else
8237   SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Plex Throughput Monitor is not supported if logging is turned off. Reconfigure using --with-log.");
8238 #endif
8239   PetscFunctionReturn(0);
8240 }
8241