xref: /petsc/src/dm/impls/plex/plex.c (revision f84dd6b4efbb17448bb09ffac202100e87e5183b)
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,DMPLEX_LocatePoints;
12 
13 PETSC_EXTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
14 
15 /*@
16   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
17 
18   Input Parameter:
19 . dm      - The DMPlex object
20 
21   Output Parameter:
22 . simplex - Flag checking for a simplex
23 
24   Note: This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
25   If the mesh has no cells, this returns PETSC_FALSE.
26 
27   Level: intermediate
28 
29 .seealso DMPlexGetSimplexOrBoxCells(), DMPlexGetCellType(), DMPlexGetHeightStratum(), DMPolytopeTypeGetNumVertices()
30 @*/
31 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
32 {
33   DMPolytopeType ct;
34   PetscInt       cStart, cEnd;
35   PetscErrorCode ierr;
36 
37   PetscFunctionBegin;
38   ierr = DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd);CHKERRQ(ierr);
39   if (cEnd <= cStart) {*simplex = PETSC_FALSE; PetscFunctionReturn(0);}
40   ierr = DMPlexGetCellType(dm, cStart, &ct);CHKERRQ(ierr);
41   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct)+1 ? PETSC_TRUE : PETSC_FALSE;
42   PetscFunctionReturn(0);
43 }
44 
45 /*@
46   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
47 
48   Input Parameter:
49 + dm     - The DMPlex object
50 - height - The cell height in the Plex, 0 is the default
51 
52   Output Parameters:
53 + cStart - The first "normal" cell
54 - cEnd   - The upper bound on "normal"" cells
55 
56   Note: This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
57 
58   Level: developer
59 
60 .seealso DMPlexConstructGhostCells(), DMPlexGetGhostCellStratum()
61 @*/
62 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd)
63 {
64   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
65   PetscInt       cS, cE, c;
66   PetscErrorCode ierr;
67 
68   PetscFunctionBegin;
69   ierr = DMPlexGetHeightStratum(dm, PetscMax(height, 0), &cS, &cE);CHKERRQ(ierr);
70   for (c = cS; c < cE; ++c) {
71     DMPolytopeType cct;
72 
73     ierr = DMPlexGetCellType(dm, c, &cct);CHKERRQ(ierr);
74     if ((PetscInt) cct < 0) break;
75     switch (cct) {
76       case DM_POLYTOPE_POINT:
77       case DM_POLYTOPE_SEGMENT:
78       case DM_POLYTOPE_TRIANGLE:
79       case DM_POLYTOPE_QUADRILATERAL:
80       case DM_POLYTOPE_TETRAHEDRON:
81       case DM_POLYTOPE_HEXAHEDRON:
82         ct = cct;
83         break;
84       default: break;
85     }
86     if (ct != DM_POLYTOPE_UNKNOWN) break;
87   }
88   if (ct != DM_POLYTOPE_UNKNOWN) {
89     DMLabel ctLabel;
90 
91     ierr = DMPlexGetCellTypeLabel(dm, &ctLabel);CHKERRQ(ierr);
92     ierr = DMLabelGetStratumBounds(ctLabel, ct, &cS, &cE);CHKERRQ(ierr);
93   }
94   if (cStart) *cStart = cS;
95   if (cEnd)   *cEnd   = cE;
96   PetscFunctionReturn(0);
97 }
98 
99 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
100 {
101   PetscInt       cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
102   PetscInt       vcdof[2] = {0,0}, globalvcdof[2];
103   PetscErrorCode ierr;
104 
105   PetscFunctionBegin;
106   *ft  = PETSC_VTK_INVALID;
107   ierr = DMGetCoordinateDim(dm, &cdim);CHKERRQ(ierr);
108   ierr = DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd);CHKERRQ(ierr);
109   ierr = DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd);CHKERRQ(ierr);
110   ierr = PetscSectionGetChart(section, &pStart, &pEnd);CHKERRQ(ierr);
111   if (field >= 0) {
112     if ((vStart >= pStart) && (vStart < pEnd)) {ierr = PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]);CHKERRQ(ierr);}
113     if ((cStart >= pStart) && (cStart < pEnd)) {ierr = PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]);CHKERRQ(ierr);}
114   } else {
115     if ((vStart >= pStart) && (vStart < pEnd)) {ierr = PetscSectionGetDof(section, vStart, &vcdof[0]);CHKERRQ(ierr);}
116     if ((cStart >= pStart) && (cStart < pEnd)) {ierr = PetscSectionGetDof(section, cStart, &vcdof[1]);CHKERRQ(ierr);}
117   }
118   ierr = MPI_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm));CHKERRMPI(ierr);
119   if (globalvcdof[0]) {
120     *sStart = vStart;
121     *sEnd   = vEnd;
122     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
123     else                        *ft = PETSC_VTK_POINT_FIELD;
124   } else if (globalvcdof[1]) {
125     *sStart = cStart;
126     *sEnd   = cEnd;
127     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
128     else                        *ft = PETSC_VTK_CELL_FIELD;
129   } else {
130     if (field >= 0) {
131       const char *fieldname;
132 
133       ierr = PetscSectionGetFieldName(section, field, &fieldname);CHKERRQ(ierr);
134       ierr = PetscInfo2((PetscObject) dm, "Could not classify VTK output type of section field %D \"%s\"\n", field, fieldname);CHKERRQ(ierr);
135     } else {
136       ierr = PetscInfo((PetscObject) dm, "Could not classify VTK output type of section\"%s\"\n");CHKERRQ(ierr);
137     }
138   }
139   PetscFunctionReturn(0);
140 }
141 
142 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
143 {
144   DM                 dm;
145   PetscSection       s;
146   PetscDraw          draw, popup;
147   DM                 cdm;
148   PetscSection       coordSection;
149   Vec                coordinates;
150   const PetscScalar *coords, *array;
151   PetscReal          bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
152   PetscReal          vbound[2], time;
153   PetscBool          isnull, flg;
154   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
155   const char        *name;
156   char               title[PETSC_MAX_PATH_LEN];
157   PetscErrorCode     ierr;
158 
159   PetscFunctionBegin;
160   ierr = PetscViewerDrawGetDraw(viewer, 0, &draw);CHKERRQ(ierr);
161   ierr = PetscDrawIsNull(draw, &isnull);CHKERRQ(ierr);
162   if (isnull) PetscFunctionReturn(0);
163 
164   ierr = VecGetDM(v, &dm);CHKERRQ(ierr);
165   ierr = DMGetCoordinateDim(dm, &dim);CHKERRQ(ierr);
166   if (dim != 2) SETERRQ1(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %D. Use PETSCVIEWERGLVIS", dim);
167   ierr = DMGetLocalSection(dm, &s);CHKERRQ(ierr);
168   ierr = PetscSectionGetNumFields(s, &Nf);CHKERRQ(ierr);
169   ierr = DMGetCoarsenLevel(dm, &level);CHKERRQ(ierr);
170   ierr = DMGetCoordinateDM(dm, &cdm);CHKERRQ(ierr);
171   ierr = DMGetLocalSection(cdm, &coordSection);CHKERRQ(ierr);
172   ierr = DMGetCoordinatesLocal(dm, &coordinates);CHKERRQ(ierr);
173   ierr = DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd);CHKERRQ(ierr);
174   ierr = DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd);CHKERRQ(ierr);
175 
176   ierr = PetscObjectGetName((PetscObject) v, &name);CHKERRQ(ierr);
177   ierr = DMGetOutputSequenceNumber(dm, &step, &time);CHKERRQ(ierr);
178 
179   ierr = VecGetLocalSize(coordinates, &N);CHKERRQ(ierr);
180   ierr = VecGetArrayRead(coordinates, &coords);CHKERRQ(ierr);
181   for (c = 0; c < N; c += dim) {
182     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));   bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
183     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c+1])); bound[3] = PetscMax(bound[3], PetscRealPart(coords[c+1]));
184   }
185   ierr = VecRestoreArrayRead(coordinates, &coords);CHKERRQ(ierr);
186   ierr = PetscDrawClear(draw);CHKERRQ(ierr);
187 
188   /* Could implement something like DMDASelectFields() */
189   for (f = 0; f < Nf; ++f) {
190     DM   fdm = dm;
191     Vec  fv  = v;
192     IS   fis;
193     char prefix[PETSC_MAX_PATH_LEN];
194     const char *fname;
195 
196     ierr = PetscSectionGetFieldComponents(s, f, &Nc);CHKERRQ(ierr);
197     ierr = PetscSectionGetFieldName(s, f, &fname);CHKERRQ(ierr);
198 
199     if (v->hdr.prefix) {ierr = PetscStrncpy(prefix, v->hdr.prefix,sizeof(prefix));CHKERRQ(ierr);}
200     else               {prefix[0] = '\0';}
201     if (Nf > 1) {
202       ierr = DMCreateSubDM(dm, 1, &f, &fis, &fdm);CHKERRQ(ierr);
203       ierr = VecGetSubVector(v, fis, &fv);CHKERRQ(ierr);
204       ierr = PetscStrlcat(prefix, fname,sizeof(prefix));CHKERRQ(ierr);
205       ierr = PetscStrlcat(prefix, "_",sizeof(prefix));CHKERRQ(ierr);
206     }
207     for (comp = 0; comp < Nc; ++comp, ++w) {
208       PetscInt nmax = 2;
209 
210       ierr = PetscViewerDrawGetDraw(viewer, w, &draw);CHKERRQ(ierr);
211       if (Nc > 1) {ierr = PetscSNPrintf(title, sizeof(title), "%s:%s_%D Step: %D Time: %.4g", name, fname, comp, step, time);CHKERRQ(ierr);}
212       else        {ierr = PetscSNPrintf(title, sizeof(title), "%s:%s Step: %D Time: %.4g", name, fname, step, time);CHKERRQ(ierr);}
213       ierr = PetscDrawSetTitle(draw, title);CHKERRQ(ierr);
214 
215       /* TODO Get max and min only for this component */
216       ierr = PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg);CHKERRQ(ierr);
217       if (!flg) {
218         ierr = VecMin(fv, NULL, &vbound[0]);CHKERRQ(ierr);
219         ierr = VecMax(fv, NULL, &vbound[1]);CHKERRQ(ierr);
220         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
221       }
222       ierr = PetscDrawGetPopup(draw, &popup);CHKERRQ(ierr);
223       ierr = PetscDrawScalePopup(popup, vbound[0], vbound[1]);CHKERRQ(ierr);
224       ierr = PetscDrawSetCoordinates(draw, bound[0], bound[1], bound[2], bound[3]);CHKERRQ(ierr);
225 
226       ierr = VecGetArrayRead(fv, &array);CHKERRQ(ierr);
227       for (c = cStart; c < cEnd; ++c) {
228         PetscScalar *coords = NULL, *a = NULL;
229         PetscInt     numCoords, color[4] = {-1,-1,-1,-1};
230 
231         ierr = DMPlexPointLocalRead(fdm, c, array, &a);CHKERRQ(ierr);
232         if (a) {
233           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
234           color[1] = color[2] = color[3] = color[0];
235         } else {
236           PetscScalar *vals = NULL;
237           PetscInt     numVals, va;
238 
239           ierr = DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals);CHKERRQ(ierr);
240           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);
241           switch (numVals/Nc) {
242           case 3: /* P1 Triangle */
243           case 4: /* P1 Quadrangle */
244             for (va = 0; va < numVals/Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va*Nc+comp]), vbound[0], vbound[1]);
245             break;
246           case 6: /* P2 Triangle */
247           case 8: /* P2 Quadrangle */
248             for (va = 0; va < numVals/(Nc*2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va*Nc+comp + numVals/(Nc*2)]), vbound[0], vbound[1]);
249             break;
250           default: SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %D cannot be handled", numVals/Nc);
251           }
252           ierr = DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals);CHKERRQ(ierr);
253         }
254         ierr = DMPlexVecGetClosure(dm, coordSection, coordinates, c, &numCoords, &coords);CHKERRQ(ierr);
255         switch (numCoords) {
256         case 6:
257           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);
258           break;
259         case 8:
260           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);
261           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);
262           break;
263         default: SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %D coordinates", numCoords);
264         }
265         ierr = DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords);CHKERRQ(ierr);
266       }
267       ierr = VecRestoreArrayRead(fv, &array);CHKERRQ(ierr);
268       ierr = PetscDrawFlush(draw);CHKERRQ(ierr);
269       ierr = PetscDrawPause(draw);CHKERRQ(ierr);
270       ierr = PetscDrawSave(draw);CHKERRQ(ierr);
271     }
272     if (Nf > 1) {
273       ierr = VecRestoreSubVector(v, fis, &fv);CHKERRQ(ierr);
274       ierr = ISDestroy(&fis);CHKERRQ(ierr);
275       ierr = DMDestroy(&fdm);CHKERRQ(ierr);
276     }
277   }
278   PetscFunctionReturn(0);
279 }
280 
281 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
282 {
283   DM                      dm;
284   Vec                     locv;
285   const char              *name;
286   PetscSection            section;
287   PetscInt                pStart, pEnd;
288   PetscInt                numFields;
289   PetscViewerVTKFieldType ft;
290   PetscErrorCode          ierr;
291 
292   PetscFunctionBegin;
293   ierr = VecGetDM(v, &dm);CHKERRQ(ierr);
294   ierr = DMCreateLocalVector(dm, &locv);CHKERRQ(ierr); /* VTK viewer requires exclusive ownership of the vector */
295   ierr = PetscObjectGetName((PetscObject) v, &name);CHKERRQ(ierr);
296   ierr = PetscObjectSetName((PetscObject) locv, name);CHKERRQ(ierr);
297   ierr = VecCopy(v, locv);CHKERRQ(ierr);
298   ierr = DMGetLocalSection(dm, &section);CHKERRQ(ierr);
299   ierr = PetscSectionGetNumFields(section, &numFields);CHKERRQ(ierr);
300   if (!numFields) {
301     ierr = DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft);CHKERRQ(ierr);
302     ierr = PetscViewerVTKAddField(viewer, (PetscObject) dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE,(PetscObject) locv);CHKERRQ(ierr);
303   } else {
304     PetscInt f;
305 
306     for (f = 0; f < numFields; f++) {
307       ierr = DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft);CHKERRQ(ierr);
308       if (ft == PETSC_VTK_INVALID) continue;
309       ierr = PetscObjectReference((PetscObject)locv);CHKERRQ(ierr);
310       ierr = PetscViewerVTKAddField(viewer, (PetscObject) dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE,(PetscObject) locv);CHKERRQ(ierr);
311     }
312     ierr = VecDestroy(&locv);CHKERRQ(ierr);
313   }
314   PetscFunctionReturn(0);
315 }
316 
317 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
318 {
319   DM             dm;
320   PetscBool      isvtk, ishdf5, isdraw, isglvis;
321   PetscErrorCode ierr;
322 
323   PetscFunctionBegin;
324   ierr = VecGetDM(v, &dm);CHKERRQ(ierr);
325   if (!dm) SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
326   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,   &isvtk);CHKERRQ(ierr);
327   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,  &ishdf5);CHKERRQ(ierr);
328   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERDRAW,  &isdraw);CHKERRQ(ierr);
329   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERGLVIS, &isglvis);CHKERRQ(ierr);
330   if (isvtk || ishdf5 || isdraw || isglvis) {
331     PetscInt    i,numFields;
332     PetscObject fe;
333     PetscBool   fem = PETSC_FALSE;
334     Vec         locv = v;
335     const char  *name;
336     PetscInt    step;
337     PetscReal   time;
338 
339     ierr = DMGetNumFields(dm, &numFields);CHKERRQ(ierr);
340     for (i=0; i<numFields; i++) {
341       ierr = DMGetField(dm, i, NULL, &fe);CHKERRQ(ierr);
342       if (fe->classid == PETSCFE_CLASSID) { fem = PETSC_TRUE; break; }
343     }
344     if (fem) {
345       PetscObject isZero;
346 
347       ierr = DMGetLocalVector(dm, &locv);CHKERRQ(ierr);
348       ierr = PetscObjectGetName((PetscObject) v, &name);CHKERRQ(ierr);
349       ierr = PetscObjectSetName((PetscObject) locv, name);CHKERRQ(ierr);
350       ierr = PetscObjectQuery((PetscObject) v, "__Vec_bc_zero__", &isZero);CHKERRQ(ierr);
351       ierr = PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", isZero);CHKERRQ(ierr);
352       ierr = VecCopy(v, locv);CHKERRQ(ierr);
353       ierr = DMGetOutputSequenceNumber(dm, NULL, &time);CHKERRQ(ierr);
354       ierr = DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL);CHKERRQ(ierr);
355     }
356     if (isvtk) {
357       ierr = VecView_Plex_Local_VTK(locv, viewer);CHKERRQ(ierr);
358     } else if (ishdf5) {
359 #if defined(PETSC_HAVE_HDF5)
360       ierr = VecView_Plex_Local_HDF5_Internal(locv, viewer);CHKERRQ(ierr);
361 #else
362       SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
363 #endif
364     } else if (isdraw) {
365       ierr = VecView_Plex_Local_Draw(locv, viewer);CHKERRQ(ierr);
366     } else if (isglvis) {
367       ierr = DMGetOutputSequenceNumber(dm, &step, NULL);CHKERRQ(ierr);
368       ierr = PetscViewerGLVisSetSnapId(viewer, step);CHKERRQ(ierr);
369       ierr = VecView_GLVis(locv, viewer);CHKERRQ(ierr);
370     }
371     if (fem) {
372       ierr = PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", NULL);CHKERRQ(ierr);
373       ierr = DMRestoreLocalVector(dm, &locv);CHKERRQ(ierr);
374     }
375   } else {
376     PetscBool isseq;
377 
378     ierr = PetscObjectTypeCompare((PetscObject) v, VECSEQ, &isseq);CHKERRQ(ierr);
379     if (isseq) {ierr = VecView_Seq(v, viewer);CHKERRQ(ierr);}
380     else       {ierr = VecView_MPI(v, viewer);CHKERRQ(ierr);}
381   }
382   PetscFunctionReturn(0);
383 }
384 
385 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
386 {
387   DM             dm;
388   PetscBool      isvtk, ishdf5, isdraw, isglvis, isexodusii;
389   PetscErrorCode ierr;
390 
391   PetscFunctionBegin;
392   ierr = VecGetDM(v, &dm);CHKERRQ(ierr);
393   if (!dm) SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
394   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,      &isvtk);CHKERRQ(ierr);
395   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,     &ishdf5);CHKERRQ(ierr);
396   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERDRAW,     &isdraw);CHKERRQ(ierr);
397   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERGLVIS,    &isglvis);CHKERRQ(ierr);
398   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWEREXODUSII, &isexodusii);CHKERRQ(ierr);
399   if (isvtk || isdraw || isglvis) {
400     Vec         locv;
401     PetscObject isZero;
402     const char *name;
403 
404     ierr = DMGetLocalVector(dm, &locv);CHKERRQ(ierr);
405     ierr = PetscObjectGetName((PetscObject) v, &name);CHKERRQ(ierr);
406     ierr = PetscObjectSetName((PetscObject) locv, name);CHKERRQ(ierr);
407     ierr = DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv);CHKERRQ(ierr);
408     ierr = DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv);CHKERRQ(ierr);
409     ierr = PetscObjectQuery((PetscObject) v, "__Vec_bc_zero__", &isZero);CHKERRQ(ierr);
410     ierr = PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", isZero);CHKERRQ(ierr);
411     ierr = VecView_Plex_Local(locv, viewer);CHKERRQ(ierr);
412     ierr = PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", NULL);CHKERRQ(ierr);
413     ierr = DMRestoreLocalVector(dm, &locv);CHKERRQ(ierr);
414   } else if (ishdf5) {
415 #if defined(PETSC_HAVE_HDF5)
416     ierr = VecView_Plex_HDF5_Internal(v, viewer);CHKERRQ(ierr);
417 #else
418     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
419 #endif
420   } else if (isexodusii) {
421 #if defined(PETSC_HAVE_EXODUSII)
422     ierr = VecView_PlexExodusII_Internal(v, viewer);CHKERRQ(ierr);
423 #else
424     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
425 #endif
426   } else {
427     PetscBool isseq;
428 
429     ierr = PetscObjectTypeCompare((PetscObject) v, VECSEQ, &isseq);CHKERRQ(ierr);
430     if (isseq) {ierr = VecView_Seq(v, viewer);CHKERRQ(ierr);}
431     else       {ierr = VecView_MPI(v, viewer);CHKERRQ(ierr);}
432   }
433   PetscFunctionReturn(0);
434 }
435 
436 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
437 {
438   DM                dm;
439   MPI_Comm          comm;
440   PetscViewerFormat format;
441   Vec               v;
442   PetscBool         isvtk, ishdf5;
443   PetscErrorCode    ierr;
444 
445   PetscFunctionBegin;
446   ierr = VecGetDM(originalv, &dm);CHKERRQ(ierr);
447   ierr = PetscObjectGetComm((PetscObject) originalv, &comm);CHKERRQ(ierr);
448   if (!dm) SETERRQ(comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
449   ierr = PetscViewerGetFormat(viewer, &format);CHKERRQ(ierr);
450   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5);CHKERRQ(ierr);
451   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,  &isvtk);CHKERRQ(ierr);
452   if (format == PETSC_VIEWER_NATIVE) {
453     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
454     /* this need a better fix */
455     if (dm->useNatural) {
456       if (dm->sfNatural) {
457         const char *vecname;
458         PetscInt    n, nroots;
459 
460         ierr = VecGetLocalSize(originalv, &n);CHKERRQ(ierr);
461         ierr = PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL);CHKERRQ(ierr);
462         if (n == nroots) {
463           ierr = DMGetGlobalVector(dm, &v);CHKERRQ(ierr);
464           ierr = DMPlexGlobalToNaturalBegin(dm, originalv, v);CHKERRQ(ierr);
465           ierr = DMPlexGlobalToNaturalEnd(dm, originalv, v);CHKERRQ(ierr);
466           ierr = PetscObjectGetName((PetscObject) originalv, &vecname);CHKERRQ(ierr);
467           ierr = PetscObjectSetName((PetscObject) v, vecname);CHKERRQ(ierr);
468         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
469       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
470     } else v = originalv;
471   } else v = originalv;
472 
473   if (ishdf5) {
474 #if defined(PETSC_HAVE_HDF5)
475     ierr = VecView_Plex_HDF5_Native_Internal(v, viewer);CHKERRQ(ierr);
476 #else
477     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
478 #endif
479   } else if (isvtk) {
480     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
481   } else {
482     PetscBool isseq;
483 
484     ierr = PetscObjectTypeCompare((PetscObject) v, VECSEQ, &isseq);CHKERRQ(ierr);
485     if (isseq) {ierr = VecView_Seq(v, viewer);CHKERRQ(ierr);}
486     else       {ierr = VecView_MPI(v, viewer);CHKERRQ(ierr);}
487   }
488   if (v != originalv) {ierr = DMRestoreGlobalVector(dm, &v);CHKERRQ(ierr);}
489   PetscFunctionReturn(0);
490 }
491 
492 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
493 {
494   DM             dm;
495   PetscBool      ishdf5;
496   PetscErrorCode ierr;
497 
498   PetscFunctionBegin;
499   ierr = VecGetDM(v, &dm);CHKERRQ(ierr);
500   if (!dm) SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
501   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5);CHKERRQ(ierr);
502   if (ishdf5) {
503     DM          dmBC;
504     Vec         gv;
505     const char *name;
506 
507     ierr = DMGetOutputDM(dm, &dmBC);CHKERRQ(ierr);
508     ierr = DMGetGlobalVector(dmBC, &gv);CHKERRQ(ierr);
509     ierr = PetscObjectGetName((PetscObject) v, &name);CHKERRQ(ierr);
510     ierr = PetscObjectSetName((PetscObject) gv, name);CHKERRQ(ierr);
511     ierr = VecLoad_Default(gv, viewer);CHKERRQ(ierr);
512     ierr = DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v);CHKERRQ(ierr);
513     ierr = DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v);CHKERRQ(ierr);
514     ierr = DMRestoreGlobalVector(dmBC, &gv);CHKERRQ(ierr);
515   } else {
516     ierr = VecLoad_Default(v, viewer);CHKERRQ(ierr);
517   }
518   PetscFunctionReturn(0);
519 }
520 
521 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
522 {
523   DM             dm;
524   PetscBool      ishdf5,isexodusii;
525   PetscErrorCode ierr;
526 
527   PetscFunctionBegin;
528   ierr = VecGetDM(v, &dm);CHKERRQ(ierr);
529   if (!dm) SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
530   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,     &ishdf5);CHKERRQ(ierr);
531   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWEREXODUSII, &isexodusii);CHKERRQ(ierr);
532   if (ishdf5) {
533 #if defined(PETSC_HAVE_HDF5)
534     ierr = VecLoad_Plex_HDF5_Internal(v, viewer);CHKERRQ(ierr);
535 #else
536     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
537 #endif
538   } else if (isexodusii) {
539 #if defined(PETSC_HAVE_EXODUSII)
540     ierr = VecLoad_PlexExodusII_Internal(v, viewer);CHKERRQ(ierr);
541 #else
542     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
543 #endif
544   } else {
545     ierr = VecLoad_Default(v, viewer);CHKERRQ(ierr);
546   }
547   PetscFunctionReturn(0);
548 }
549 
550 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
551 {
552   DM                dm;
553   PetscViewerFormat format;
554   PetscBool         ishdf5;
555   PetscErrorCode    ierr;
556 
557   PetscFunctionBegin;
558   ierr = VecGetDM(originalv, &dm);CHKERRQ(ierr);
559   if (!dm) SETERRQ(PetscObjectComm((PetscObject) originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
560   ierr = PetscViewerGetFormat(viewer, &format);CHKERRQ(ierr);
561   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5);CHKERRQ(ierr);
562   if (format == PETSC_VIEWER_NATIVE) {
563     if (dm->useNatural) {
564       if (dm->sfNatural) {
565         if (ishdf5) {
566 #if defined(PETSC_HAVE_HDF5)
567           Vec         v;
568           const char *vecname;
569 
570           ierr = DMGetGlobalVector(dm, &v);CHKERRQ(ierr);
571           ierr = PetscObjectGetName((PetscObject) originalv, &vecname);CHKERRQ(ierr);
572           ierr = PetscObjectSetName((PetscObject) v, vecname);CHKERRQ(ierr);
573           ierr = VecLoad_Plex_HDF5_Native_Internal(v, viewer);CHKERRQ(ierr);
574           ierr = DMPlexNaturalToGlobalBegin(dm, v, originalv);CHKERRQ(ierr);
575           ierr = DMPlexNaturalToGlobalEnd(dm, v, originalv);CHKERRQ(ierr);
576           ierr = DMRestoreGlobalVector(dm, &v);CHKERRQ(ierr);
577 #else
578           SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
579 #endif
580         } else SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
581       }
582     } else {
583       ierr = VecLoad_Default(originalv, viewer);CHKERRQ(ierr);
584     }
585   }
586   PetscFunctionReturn(0);
587 }
588 
589 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
590 {
591   PetscSection       coordSection;
592   Vec                coordinates;
593   DMLabel            depthLabel, celltypeLabel;
594   const char        *name[4];
595   const PetscScalar *a;
596   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
597   PetscErrorCode     ierr;
598 
599   PetscFunctionBegin;
600   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
601   ierr = DMGetCoordinatesLocal(dm, &coordinates);CHKERRQ(ierr);
602   ierr = DMGetCoordinateSection(dm, &coordSection);CHKERRQ(ierr);
603   ierr = DMPlexGetDepthLabel(dm, &depthLabel);CHKERRQ(ierr);
604   ierr = DMPlexGetCellTypeLabel(dm, &celltypeLabel);CHKERRQ(ierr);
605   ierr = DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd);CHKERRQ(ierr);
606   ierr = PetscSectionGetChart(coordSection, &pStart, &pEnd);CHKERRQ(ierr);
607   ierr = VecGetArrayRead(coordinates, &a);CHKERRQ(ierr);
608   name[0]     = "vertex";
609   name[1]     = "edge";
610   name[dim-1] = "face";
611   name[dim]   = "cell";
612   for (c = cStart; c < cEnd; ++c) {
613     PetscInt *closure = NULL;
614     PetscInt  closureSize, cl, ct;
615 
616     ierr = DMLabelGetValue(celltypeLabel, c, &ct);CHKERRQ(ierr);
617     ierr = PetscViewerASCIIPrintf(viewer, "Geometry for cell %D polytope type %s:\n", c, DMPolytopeTypes[ct]);CHKERRQ(ierr);
618     ierr = DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure);CHKERRQ(ierr);
619     ierr = PetscViewerASCIIPushTab(viewer);CHKERRQ(ierr);
620     for (cl = 0; cl < closureSize*2; cl += 2) {
621       PetscInt point = closure[cl], depth, dof, off, d, p;
622 
623       if ((point < pStart) || (point >= pEnd)) continue;
624       ierr = PetscSectionGetDof(coordSection, point, &dof);CHKERRQ(ierr);
625       if (!dof) continue;
626       ierr = DMLabelGetValue(depthLabel, point, &depth);CHKERRQ(ierr);
627       ierr = PetscSectionGetOffset(coordSection, point, &off);CHKERRQ(ierr);
628       ierr = PetscViewerASCIIPrintf(viewer, "%s %D coords:", name[depth], point);CHKERRQ(ierr);
629       for (p = 0; p < dof/dim; ++p) {
630         ierr = PetscViewerASCIIPrintf(viewer, " (");CHKERRQ(ierr);
631         for (d = 0; d < dim; ++d) {
632           if (d > 0) {ierr = PetscViewerASCIIPrintf(viewer, ", ");CHKERRQ(ierr);}
633           ierr = PetscViewerASCIIPrintf(viewer, "%g", (double) PetscRealPart(a[off+p*dim+d]));CHKERRQ(ierr);
634         }
635         ierr = PetscViewerASCIIPrintf(viewer, ")");CHKERRQ(ierr);
636       }
637       ierr = PetscViewerASCIIPrintf(viewer, "\n");CHKERRQ(ierr);
638     }
639     ierr = DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure);CHKERRQ(ierr);
640     ierr = PetscViewerASCIIPopTab(viewer);CHKERRQ(ierr);
641   }
642   ierr = VecRestoreArrayRead(coordinates, &a);CHKERRQ(ierr);
643   PetscFunctionReturn(0);
644 }
645 
646 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
647 {
648   DM_Plex          *mesh = (DM_Plex*) dm->data;
649   DM                cdm;
650   PetscSection      coordSection;
651   Vec               coordinates;
652   PetscViewerFormat format;
653   PetscErrorCode    ierr;
654 
655   PetscFunctionBegin;
656   ierr = DMGetCoordinateDM(dm, &cdm);CHKERRQ(ierr);
657   ierr = DMGetLocalSection(cdm, &coordSection);CHKERRQ(ierr);
658   ierr = DMGetCoordinatesLocal(dm, &coordinates);CHKERRQ(ierr);
659   ierr = PetscViewerGetFormat(viewer, &format);CHKERRQ(ierr);
660   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
661     const char *name;
662     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
663     PetscInt    pStart, pEnd, p, numLabels, l;
664     PetscMPIInt rank, size;
665 
666     ierr = MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank);CHKERRMPI(ierr);
667     ierr = MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size);CHKERRMPI(ierr);
668     ierr = PetscObjectGetName((PetscObject) dm, &name);CHKERRQ(ierr);
669     ierr = DMPlexGetChart(dm, &pStart, &pEnd);CHKERRQ(ierr);
670     ierr = DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize);CHKERRQ(ierr);
671     ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
672     ierr = DMPlexGetVTKCellHeight(dm, &cellHeight);CHKERRQ(ierr);
673     if (name) {ierr = PetscViewerASCIIPrintf(viewer, "%s in %D dimension%s:\n", name, dim, dim == 1 ? "" : "s");CHKERRQ(ierr);}
674     else      {ierr = PetscViewerASCIIPrintf(viewer, "Mesh in %D dimension%s:\n", dim, dim == 1 ? "" : "s");CHKERRQ(ierr);}
675     if (cellHeight) {ierr = PetscViewerASCIIPrintf(viewer, "  Cells are at height %D\n", cellHeight);CHKERRQ(ierr);}
676     ierr = PetscViewerASCIIPrintf(viewer, "Supports:\n", name);CHKERRQ(ierr);
677     ierr = PetscViewerASCIIPushSynchronized(viewer);CHKERRQ(ierr);
678     ierr = PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %D\n", rank, maxSupportSize);CHKERRQ(ierr);
679     for (p = pStart; p < pEnd; ++p) {
680       PetscInt dof, off, s;
681 
682       ierr = PetscSectionGetDof(mesh->supportSection, p, &dof);CHKERRQ(ierr);
683       ierr = PetscSectionGetOffset(mesh->supportSection, p, &off);CHKERRQ(ierr);
684       for (s = off; s < off+dof; ++s) {
685         ierr = PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %D ----> %D\n", rank, p, mesh->supports[s]);CHKERRQ(ierr);
686       }
687     }
688     ierr = PetscViewerFlush(viewer);CHKERRQ(ierr);
689     ierr = PetscViewerASCIIPrintf(viewer, "Cones:\n", name);CHKERRQ(ierr);
690     ierr = PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %D\n", rank, maxConeSize);CHKERRQ(ierr);
691     for (p = pStart; p < pEnd; ++p) {
692       PetscInt dof, off, c;
693 
694       ierr = PetscSectionGetDof(mesh->coneSection, p, &dof);CHKERRQ(ierr);
695       ierr = PetscSectionGetOffset(mesh->coneSection, p, &off);CHKERRQ(ierr);
696       for (c = off; c < off+dof; ++c) {
697         ierr = PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %D <---- %D (%D)\n", rank, p, mesh->cones[c], mesh->coneOrientations[c]);CHKERRQ(ierr);
698       }
699     }
700     ierr = PetscViewerFlush(viewer);CHKERRQ(ierr);
701     ierr = PetscViewerASCIIPopSynchronized(viewer);CHKERRQ(ierr);
702     if (coordSection && coordinates) {
703       ierr = PetscSectionVecView(coordSection, coordinates, viewer);CHKERRQ(ierr);
704     }
705     ierr = DMGetNumLabels(dm, &numLabels);CHKERRQ(ierr);
706     if (numLabels) {ierr = PetscViewerASCIIPrintf(viewer, "Labels:\n");CHKERRQ(ierr);}
707     for (l = 0; l < numLabels; ++l) {
708       DMLabel     label;
709       PetscBool   isdepth;
710       const char *name;
711 
712       ierr = DMGetLabelName(dm, l, &name);CHKERRQ(ierr);
713       ierr = PetscStrcmp(name, "depth", &isdepth);CHKERRQ(ierr);
714       if (isdepth) continue;
715       ierr = DMGetLabel(dm, name, &label);CHKERRQ(ierr);
716       ierr = DMLabelView(label, viewer);CHKERRQ(ierr);
717     }
718     if (size > 1) {
719       PetscSF sf;
720 
721       ierr = DMGetPointSF(dm, &sf);CHKERRQ(ierr);
722       ierr = PetscSFView(sf, viewer);CHKERRQ(ierr);
723     }
724     ierr = PetscViewerFlush(viewer);CHKERRQ(ierr);
725   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
726     const char  *name, *color;
727     const char  *defcolors[3]  = {"gray", "orange", "green"};
728     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
729     char         lname[PETSC_MAX_PATH_LEN];
730     PetscReal    scale         = 2.0;
731     PetscReal    tikzscale     = 1.0;
732     PetscBool    useNumbers    = PETSC_TRUE, useLabels, useColors;
733     double       tcoords[3];
734     PetscScalar *coords;
735     PetscInt     numLabels, l, numColors, numLColors, dim, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p;
736     PetscMPIInt  rank, size;
737     char         **names, **colors, **lcolors;
738     PetscBool    plotEdges, flg, lflg;
739     PetscBT      wp = NULL;
740     PetscInt     pEnd, pStart;
741 
742     ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
743     ierr = DMPlexGetDepth(dm, &depth);CHKERRQ(ierr);
744     ierr = DMGetNumLabels(dm, &numLabels);CHKERRQ(ierr);
745     numLabels  = PetscMax(numLabels, 10);
746     numColors  = 10;
747     numLColors = 10;
748     ierr = PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors);CHKERRQ(ierr);
749     ierr = PetscOptionsGetReal(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_scale", &scale, NULL);CHKERRQ(ierr);
750     ierr = PetscOptionsGetReal(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL);CHKERRQ(ierr);
751     ierr = PetscOptionsGetBool(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL);CHKERRQ(ierr);
752     ierr = PetscOptionsGetStringArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels);CHKERRQ(ierr);
753     if (!useLabels) numLabels = 0;
754     ierr = PetscOptionsGetStringArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors);CHKERRQ(ierr);
755     if (!useColors) {
756       numColors = 3;
757       for (c = 0; c < numColors; ++c) {ierr = PetscStrallocpy(defcolors[c], &colors[c]);CHKERRQ(ierr);}
758     }
759     ierr = PetscOptionsGetStringArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors);CHKERRQ(ierr);
760     if (!useColors) {
761       numLColors = 4;
762       for (c = 0; c < numLColors; ++c) {ierr = PetscStrallocpy(deflcolors[c], &lcolors[c]);CHKERRQ(ierr);}
763     }
764     ierr = PetscOptionsGetString(((PetscObject) viewer)->options, ((PetscObject) viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg);CHKERRQ(ierr);
765     plotEdges = (PetscBool)(depth > 1 && useNumbers && dim < 3);
766     ierr = PetscOptionsGetBool(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg);CHKERRQ(ierr);
767     if (flg && plotEdges && depth < dim) SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Mesh must be interpolated");
768     if (depth < dim) plotEdges = PETSC_FALSE;
769 
770     /* filter points with labelvalue != labeldefaultvalue */
771     ierr = DMPlexGetChart(dm, &pStart, &pEnd);CHKERRQ(ierr);
772     if (lflg) {
773       DMLabel lbl;
774 
775       ierr = DMGetLabel(dm, lname, &lbl);CHKERRQ(ierr);
776       if (lbl) {
777         PetscInt val, defval;
778 
779         ierr = DMLabelGetDefaultValue(lbl, &defval);CHKERRQ(ierr);
780         ierr = PetscBTCreate(pEnd-pStart, &wp);CHKERRQ(ierr);
781         for (c = pStart;  c < pEnd; c++) {
782           PetscInt *closure = NULL;
783           PetscInt  closureSize;
784 
785           ierr = DMLabelGetValue(lbl, c, &val);CHKERRQ(ierr);
786           if (val == defval) continue;
787 
788           ierr = DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure);CHKERRQ(ierr);
789           for (p = 0; p < closureSize*2; p += 2) {
790             ierr = PetscBTSet(wp, closure[p] - pStart);CHKERRQ(ierr);
791           }
792           ierr = DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure);CHKERRQ(ierr);
793         }
794       }
795     }
796 
797     ierr = MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank);CHKERRMPI(ierr);
798     ierr = MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size);CHKERRMPI(ierr);
799     ierr = PetscObjectGetName((PetscObject) dm, &name);CHKERRQ(ierr);
800     ierr = PetscViewerASCIIPrintf(viewer, "\
801 \\documentclass[tikz]{standalone}\n\n\
802 \\usepackage{pgflibraryshapes}\n\
803 \\usetikzlibrary{backgrounds}\n\
804 \\usetikzlibrary{arrows}\n\
805 \\begin{document}\n");CHKERRQ(ierr);
806     if (size > 1) {
807       ierr = PetscViewerASCIIPrintf(viewer, "%s for process ", name);CHKERRQ(ierr);
808       for (p = 0; p < size; ++p) {
809         if (p > 0 && p == size-1) {
810           ierr = PetscViewerASCIIPrintf(viewer, ", and ", colors[p%numColors], p);CHKERRQ(ierr);
811         } else if (p > 0) {
812           ierr = PetscViewerASCIIPrintf(viewer, ", ", colors[p%numColors], p);CHKERRQ(ierr);
813         }
814         ierr = PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%D}", colors[p%numColors], p);CHKERRQ(ierr);
815       }
816       ierr = PetscViewerASCIIPrintf(viewer, ".\n\n\n");CHKERRQ(ierr);
817     }
818     ierr = PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double) tikzscale);CHKERRQ(ierr);
819 
820     /* Plot vertices */
821     ierr = DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd);CHKERRQ(ierr);
822     ierr = VecGetArray(coordinates, &coords);CHKERRQ(ierr);
823     ierr = PetscViewerASCIIPushSynchronized(viewer);CHKERRQ(ierr);
824     for (v = vStart; v < vEnd; ++v) {
825       PetscInt  off, dof, d;
826       PetscBool isLabeled = PETSC_FALSE;
827 
828       if (wp && !PetscBTLookup(wp,v - pStart)) continue;
829       ierr = PetscSectionGetDof(coordSection, v, &dof);CHKERRQ(ierr);
830       ierr = PetscSectionGetOffset(coordSection, v, &off);CHKERRQ(ierr);
831       ierr = PetscViewerASCIISynchronizedPrintf(viewer, "\\path (");CHKERRQ(ierr);
832       if (PetscUnlikely(dof > 3)) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_PLIB,"coordSection vertex %D has dof %D > 3",v,dof);
833       for (d = 0; d < dof; ++d) {
834         tcoords[d] = (double) (scale*PetscRealPart(coords[off+d]));
835         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
836       }
837       /* Rotate coordinates since PGF makes z point out of the page instead of up */
838       if (dim == 3) {PetscReal tmp = tcoords[1]; tcoords[1] = tcoords[2]; tcoords[2] = -tmp;}
839       for (d = 0; d < dof; ++d) {
840         if (d > 0) {ierr = PetscViewerASCIISynchronizedPrintf(viewer, ",");CHKERRQ(ierr);}
841         ierr = PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double) tcoords[d]);CHKERRQ(ierr);
842       }
843       color = colors[rank%numColors];
844       for (l = 0; l < numLabels; ++l) {
845         PetscInt val;
846         ierr = DMGetLabelValue(dm, names[l], v, &val);CHKERRQ(ierr);
847         if (val >= 0) {color = lcolors[l%numLColors]; isLabeled = PETSC_TRUE; break;}
848       }
849       if (useNumbers) {
850         ierr = PetscViewerASCIISynchronizedPrintf(viewer, ") node(%D_%d) [draw,shape=circle,color=%s] {%D};\n", v, rank, color, v);CHKERRQ(ierr);
851       } else {
852         ierr = PetscViewerASCIISynchronizedPrintf(viewer, ") node(%D_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color);CHKERRQ(ierr);
853       }
854     }
855     ierr = VecRestoreArray(coordinates, &coords);CHKERRQ(ierr);
856     ierr = PetscViewerFlush(viewer);CHKERRQ(ierr);
857     /* Plot cells */
858     ierr = DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd);CHKERRQ(ierr);
859     ierr = DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd);CHKERRQ(ierr);
860     if (dim == 3 || !useNumbers) {
861       for (e = eStart; e < eEnd; ++e) {
862         const PetscInt *cone;
863 
864         if (wp && !PetscBTLookup(wp,e - pStart)) continue;
865         color = colors[rank%numColors];
866         for (l = 0; l < numLabels; ++l) {
867           PetscInt val;
868           ierr = DMGetLabelValue(dm, names[l], e, &val);CHKERRQ(ierr);
869           if (val >= 0) {color = lcolors[l%numLColors]; break;}
870         }
871         ierr = DMPlexGetCone(dm, e, &cone);CHKERRQ(ierr);
872         ierr = PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%D_%d) -- (%D_%d);\n", color, cone[0], rank, cone[1], rank);CHKERRQ(ierr);
873       }
874     } else {
875       for (c = cStart; c < cEnd; ++c) {
876         PetscInt *closure = NULL;
877         PetscInt  closureSize, firstPoint = -1;
878 
879         if (wp && !PetscBTLookup(wp,c - pStart)) continue;
880         ierr = DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure);CHKERRQ(ierr);
881         ierr = PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank%numColors]);CHKERRQ(ierr);
882         for (p = 0; p < closureSize*2; p += 2) {
883           const PetscInt point = closure[p];
884 
885           if ((point < vStart) || (point >= vEnd)) continue;
886           if (firstPoint >= 0) {ierr = PetscViewerASCIISynchronizedPrintf(viewer, " -- ");CHKERRQ(ierr);}
887           ierr = PetscViewerASCIISynchronizedPrintf(viewer, "(%D_%d)", point, rank);CHKERRQ(ierr);
888           if (firstPoint < 0) firstPoint = point;
889         }
890         /* Why doesn't this work? ierr = PetscViewerASCIISynchronizedPrintf(viewer, " -- cycle;\n");CHKERRQ(ierr); */
891         ierr = PetscViewerASCIISynchronizedPrintf(viewer, " -- (%D_%d);\n", firstPoint, rank);CHKERRQ(ierr);
892         ierr = DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure);CHKERRQ(ierr);
893       }
894     }
895     ierr = VecGetArray(coordinates, &coords);CHKERRQ(ierr);
896     for (c = cStart; c < cEnd; ++c) {
897       double    ccoords[3] = {0.0, 0.0, 0.0};
898       PetscBool isLabeled  = PETSC_FALSE;
899       PetscInt *closure    = NULL;
900       PetscInt  closureSize, dof, d, n = 0;
901 
902       if (wp && !PetscBTLookup(wp,c - pStart)) continue;
903       ierr = DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure);CHKERRQ(ierr);
904       ierr = PetscViewerASCIISynchronizedPrintf(viewer, "\\path (");CHKERRQ(ierr);
905       for (p = 0; p < closureSize*2; p += 2) {
906         const PetscInt point = closure[p];
907         PetscInt       off;
908 
909         if ((point < vStart) || (point >= vEnd)) continue;
910         ierr = PetscSectionGetDof(coordSection, point, &dof);CHKERRQ(ierr);
911         ierr = PetscSectionGetOffset(coordSection, point, &off);CHKERRQ(ierr);
912         for (d = 0; d < dof; ++d) {
913           tcoords[d] = (double) (scale*PetscRealPart(coords[off+d]));
914           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
915         }
916         /* Rotate coordinates since PGF makes z point out of the page instead of up */
917         if (dof == 3) {PetscReal tmp = tcoords[1]; tcoords[1] = tcoords[2]; tcoords[2] = -tmp;}
918         for (d = 0; d < dof; ++d) {ccoords[d] += tcoords[d];}
919         ++n;
920       }
921       for (d = 0; d < dof; ++d) {ccoords[d] /= n;}
922       ierr = DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure);CHKERRQ(ierr);
923       for (d = 0; d < dof; ++d) {
924         if (d > 0) {ierr = PetscViewerASCIISynchronizedPrintf(viewer, ",");CHKERRQ(ierr);}
925         ierr = PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double) ccoords[d]);CHKERRQ(ierr);
926       }
927       color = colors[rank%numColors];
928       for (l = 0; l < numLabels; ++l) {
929         PetscInt val;
930         ierr = DMGetLabelValue(dm, names[l], c, &val);CHKERRQ(ierr);
931         if (val >= 0) {color = lcolors[l%numLColors]; isLabeled = PETSC_TRUE; break;}
932       }
933       if (useNumbers) {
934         ierr = PetscViewerASCIISynchronizedPrintf(viewer, ") node(%D_%d) [draw,shape=circle,color=%s] {%D};\n", c, rank, color, c);CHKERRQ(ierr);
935       } else {
936         ierr = PetscViewerASCIISynchronizedPrintf(viewer, ") node(%D_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color);CHKERRQ(ierr);
937       }
938     }
939     ierr = VecRestoreArray(coordinates, &coords);CHKERRQ(ierr);
940     /* Plot edges */
941     if (plotEdges) {
942       ierr = VecGetArray(coordinates, &coords);CHKERRQ(ierr);
943       ierr = PetscViewerASCIIPrintf(viewer, "\\path\n");CHKERRQ(ierr);
944       for (e = eStart; e < eEnd; ++e) {
945         const PetscInt *cone;
946         PetscInt        coneSize, offA, offB, dof, d;
947 
948         if (wp && !PetscBTLookup(wp,e - pStart)) continue;
949         ierr = DMPlexGetConeSize(dm, e, &coneSize);CHKERRQ(ierr);
950         if (coneSize != 2) SETERRQ2(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %D cone should have two vertices, not %D", e, coneSize);
951         ierr = DMPlexGetCone(dm, e, &cone);CHKERRQ(ierr);
952         ierr = PetscSectionGetDof(coordSection, cone[0], &dof);CHKERRQ(ierr);
953         ierr = PetscSectionGetOffset(coordSection, cone[0], &offA);CHKERRQ(ierr);
954         ierr = PetscSectionGetOffset(coordSection, cone[1], &offB);CHKERRQ(ierr);
955         ierr = PetscViewerASCIISynchronizedPrintf(viewer, "(");CHKERRQ(ierr);
956         for (d = 0; d < dof; ++d) {
957           tcoords[d] = (double) (0.5*scale*PetscRealPart(coords[offA+d]+coords[offB+d]));
958           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
959         }
960         /* Rotate coordinates since PGF makes z point out of the page instead of up */
961         if (dim == 3) {PetscReal tmp = tcoords[1]; tcoords[1] = tcoords[2]; tcoords[2] = -tmp;}
962         for (d = 0; d < dof; ++d) {
963           if (d > 0) {ierr = PetscViewerASCIISynchronizedPrintf(viewer, ",");CHKERRQ(ierr);}
964           ierr = PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]);CHKERRQ(ierr);
965         }
966         color = colors[rank%numColors];
967         for (l = 0; l < numLabels; ++l) {
968           PetscInt val;
969           ierr = DMGetLabelValue(dm, names[l], v, &val);CHKERRQ(ierr);
970           if (val >= 0) {color = lcolors[l%numLColors]; break;}
971         }
972         ierr = PetscViewerASCIISynchronizedPrintf(viewer, ") node(%D_%d) [draw,shape=circle,color=%s] {%D} --\n", e, rank, color, e);CHKERRQ(ierr);
973       }
974       ierr = VecRestoreArray(coordinates, &coords);CHKERRQ(ierr);
975       ierr = PetscViewerFlush(viewer);CHKERRQ(ierr);
976       ierr = PetscViewerASCIIPrintf(viewer, "(0,0);\n");CHKERRQ(ierr);
977     }
978     ierr = PetscViewerFlush(viewer);CHKERRQ(ierr);
979     ierr = PetscViewerASCIIPopSynchronized(viewer);CHKERRQ(ierr);
980     ierr = PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n");CHKERRQ(ierr);
981     ierr = PetscViewerASCIIPrintf(viewer, "\\end{document}\n", name);CHKERRQ(ierr);
982     for (l = 0; l < numLabels;  ++l) {ierr = PetscFree(names[l]);CHKERRQ(ierr);}
983     for (c = 0; c < numColors;  ++c) {ierr = PetscFree(colors[c]);CHKERRQ(ierr);}
984     for (c = 0; c < numLColors; ++c) {ierr = PetscFree(lcolors[c]);CHKERRQ(ierr);}
985     ierr = PetscFree3(names, colors, lcolors);CHKERRQ(ierr);
986     ierr = PetscBTDestroy(&wp);CHKERRQ(ierr);
987   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
988     Vec                    cown,acown;
989     VecScatter             sct;
990     ISLocalToGlobalMapping g2l;
991     IS                     gid,acis;
992     MPI_Comm               comm,ncomm = MPI_COMM_NULL;
993     MPI_Group              ggroup,ngroup;
994     PetscScalar            *array,nid;
995     const PetscInt         *idxs;
996     PetscInt               *idxs2,*start,*adjacency,*work;
997     PetscInt64             lm[3],gm[3];
998     PetscInt               i,c,cStart,cEnd,cum,numVertices,ect,ectn,cellHeight;
999     PetscMPIInt            d1,d2,rank;
1000 
1001     ierr = PetscObjectGetComm((PetscObject)dm,&comm);CHKERRQ(ierr);
1002     ierr = MPI_Comm_rank(comm,&rank);CHKERRMPI(ierr);
1003 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1004     ierr = MPI_Comm_split_type(comm,MPI_COMM_TYPE_SHARED,rank,MPI_INFO_NULL,&ncomm);CHKERRMPI(ierr);
1005 #endif
1006     if (ncomm != MPI_COMM_NULL) {
1007       ierr = MPI_Comm_group(comm,&ggroup);CHKERRMPI(ierr);
1008       ierr = MPI_Comm_group(ncomm,&ngroup);CHKERRMPI(ierr);
1009       d1   = 0;
1010       ierr = MPI_Group_translate_ranks(ngroup,1,&d1,ggroup,&d2);CHKERRMPI(ierr);
1011       nid  = d2;
1012       ierr = MPI_Group_free(&ggroup);CHKERRMPI(ierr);
1013       ierr = MPI_Group_free(&ngroup);CHKERRMPI(ierr);
1014       ierr = MPI_Comm_free(&ncomm);CHKERRMPI(ierr);
1015     } else nid = 0.0;
1016 
1017     /* Get connectivity */
1018     ierr = DMPlexGetVTKCellHeight(dm,&cellHeight);CHKERRQ(ierr);
1019     ierr = DMPlexCreatePartitionerGraph(dm,cellHeight,&numVertices,&start,&adjacency,&gid);CHKERRQ(ierr);
1020 
1021     /* filter overlapped local cells */
1022     ierr = DMPlexGetHeightStratum(dm,cellHeight,&cStart,&cEnd);CHKERRQ(ierr);
1023     ierr = ISGetIndices(gid,&idxs);CHKERRQ(ierr);
1024     ierr = ISGetLocalSize(gid,&cum);CHKERRQ(ierr);
1025     ierr = PetscMalloc1(cum,&idxs2);CHKERRQ(ierr);
1026     for (c = cStart, cum = 0; c < cEnd; c++) {
1027       if (idxs[c-cStart] < 0) continue;
1028       idxs2[cum++] = idxs[c-cStart];
1029     }
1030     ierr = ISRestoreIndices(gid,&idxs);CHKERRQ(ierr);
1031     if (numVertices != cum) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_PLIB,"Unexpected %D != %D",numVertices,cum);
1032     ierr = ISDestroy(&gid);CHKERRQ(ierr);
1033     ierr = ISCreateGeneral(comm,numVertices,idxs2,PETSC_OWN_POINTER,&gid);CHKERRQ(ierr);
1034 
1035     /* support for node-aware cell locality */
1036     ierr = ISCreateGeneral(comm,start[numVertices],adjacency,PETSC_USE_POINTER,&acis);CHKERRQ(ierr);
1037     ierr = VecCreateSeq(PETSC_COMM_SELF,start[numVertices],&acown);CHKERRQ(ierr);
1038     ierr = VecCreateMPI(comm,numVertices,PETSC_DECIDE,&cown);CHKERRQ(ierr);
1039     ierr = VecGetArray(cown,&array);CHKERRQ(ierr);
1040     for (c = 0; c < numVertices; c++) array[c] = nid;
1041     ierr = VecRestoreArray(cown,&array);CHKERRQ(ierr);
1042     ierr = VecScatterCreate(cown,acis,acown,NULL,&sct);CHKERRQ(ierr);
1043     ierr = VecScatterBegin(sct,cown,acown,INSERT_VALUES,SCATTER_FORWARD);CHKERRQ(ierr);
1044     ierr = VecScatterEnd(sct,cown,acown,INSERT_VALUES,SCATTER_FORWARD);CHKERRQ(ierr);
1045     ierr = ISDestroy(&acis);CHKERRQ(ierr);
1046     ierr = VecScatterDestroy(&sct);CHKERRQ(ierr);
1047     ierr = VecDestroy(&cown);CHKERRQ(ierr);
1048 
1049     /* compute edgeCut */
1050     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum,start[c+1]-start[c]);
1051     ierr = PetscMalloc1(cum,&work);CHKERRQ(ierr);
1052     ierr = ISLocalToGlobalMappingCreateIS(gid,&g2l);CHKERRQ(ierr);
1053     ierr = ISLocalToGlobalMappingSetType(g2l,ISLOCALTOGLOBALMAPPINGHASH);CHKERRQ(ierr);
1054     ierr = ISDestroy(&gid);CHKERRQ(ierr);
1055     ierr = VecGetArray(acown,&array);CHKERRQ(ierr);
1056     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1057       PetscInt totl;
1058 
1059       totl = start[c+1]-start[c];
1060       ierr = ISGlobalToLocalMappingApply(g2l,IS_GTOLM_MASK,totl,adjacency+start[c],NULL,work);CHKERRQ(ierr);
1061       for (i = 0; i < totl; i++) {
1062         if (work[i] < 0) {
1063           ect  += 1;
1064           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1065         }
1066       }
1067     }
1068     ierr  = PetscFree(work);CHKERRQ(ierr);
1069     ierr  = VecRestoreArray(acown,&array);CHKERRQ(ierr);
1070     lm[0] = numVertices > 0 ?  numVertices : PETSC_MAX_INT;
1071     lm[1] = -numVertices;
1072     ierr  = MPIU_Allreduce(lm,gm,2,MPIU_INT64,MPI_MIN,comm);CHKERRMPI(ierr);
1073     ierr  = PetscViewerASCIIPrintf(viewer,"  Cell balance: %.2f (max %D, min %D",-((double)gm[1])/((double)gm[0]),-(PetscInt)gm[1],(PetscInt)gm[0]);CHKERRQ(ierr);
1074     lm[0] = ect; /* edgeCut */
1075     lm[1] = ectn; /* node-aware edgeCut */
1076     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1077     ierr  = MPIU_Allreduce(lm,gm,3,MPIU_INT64,MPI_SUM,comm);CHKERRMPI(ierr);
1078     ierr  = PetscViewerASCIIPrintf(viewer,", empty %D)\n",(PetscInt)gm[2]);CHKERRQ(ierr);
1079 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1080     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);
1081 #else
1082     ierr  = PetscViewerASCIIPrintf(viewer,"  Edge Cut: %D (on node %.3f)\n",(PetscInt)(gm[0]/2),0.0);CHKERRQ(ierr);
1083 #endif
1084     ierr  = ISLocalToGlobalMappingDestroy(&g2l);CHKERRQ(ierr);
1085     ierr  = PetscFree(start);CHKERRQ(ierr);
1086     ierr  = PetscFree(adjacency);CHKERRQ(ierr);
1087     ierr  = VecDestroy(&acown);CHKERRQ(ierr);
1088   } else {
1089     const char    *name;
1090     PetscInt      *sizes, *hybsizes, *ghostsizes;
1091     PetscInt       locDepth, depth, cellHeight, dim, d;
1092     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1093     PetscInt       numLabels, l;
1094     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1095     MPI_Comm       comm;
1096     PetscMPIInt    size, rank;
1097 
1098     ierr = PetscObjectGetComm((PetscObject) dm, &comm);CHKERRQ(ierr);
1099     ierr = MPI_Comm_size(comm, &size);CHKERRMPI(ierr);
1100     ierr = MPI_Comm_rank(comm, &rank);CHKERRMPI(ierr);
1101     ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
1102     ierr = DMPlexGetVTKCellHeight(dm, &cellHeight);CHKERRQ(ierr);
1103     ierr = PetscObjectGetName((PetscObject) dm, &name);CHKERRQ(ierr);
1104     if (name) {ierr = PetscViewerASCIIPrintf(viewer, "%s in %D dimension%s:\n", name, dim, dim == 1 ? "" : "s");CHKERRQ(ierr);}
1105     else      {ierr = PetscViewerASCIIPrintf(viewer, "Mesh in %D dimension%s:\n", dim, dim == 1 ? "" : "s");CHKERRQ(ierr);}
1106     if (cellHeight) {ierr = PetscViewerASCIIPrintf(viewer, "  Cells are at height %D\n", cellHeight);CHKERRQ(ierr);}
1107     ierr = DMPlexGetDepth(dm, &locDepth);CHKERRQ(ierr);
1108     ierr = MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm);CHKERRMPI(ierr);
1109     ierr = DMPlexGetGhostCellStratum(dm, &gcStart, &gcEnd);CHKERRQ(ierr);
1110     gcNum = gcEnd - gcStart;
1111     ierr = PetscCalloc3(size,&sizes,size,&hybsizes,size,&ghostsizes);CHKERRQ(ierr);
1112     for (d = 0; d <= depth; d++) {
1113       PetscInt Nc[2] = {0, 0}, ict;
1114 
1115       ierr = DMPlexGetDepthStratum(dm, d, &pStart, &pEnd);CHKERRQ(ierr);
1116       if (pStart < pEnd) {ierr = DMPlexGetCellType(dm, pStart, &ct0);CHKERRQ(ierr);}
1117       ict  = ct0;
1118       ierr = MPI_Bcast(&ict, 1, MPIU_INT, 0, comm);CHKERRMPI(ierr);
1119       ct0  = (DMPolytopeType) ict;
1120       for (p = pStart; p < pEnd; ++p) {
1121         DMPolytopeType ct;
1122 
1123         ierr = DMPlexGetCellType(dm, p, &ct);CHKERRQ(ierr);
1124         if (ct == ct0) ++Nc[0];
1125         else           ++Nc[1];
1126       }
1127       ierr = MPI_Gather(&Nc[0], 1, MPIU_INT, sizes,    1, MPIU_INT, 0, comm);CHKERRMPI(ierr);
1128       ierr = MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm);CHKERRMPI(ierr);
1129       if (d == depth) {ierr = MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm);CHKERRMPI(ierr);}
1130       ierr = PetscViewerASCIIPrintf(viewer, "  %D-cells:", (depth == 1) && d ? dim : d);CHKERRQ(ierr);
1131       for (p = 0; p < size; ++p) {
1132         if (!rank) {
1133           ierr = PetscViewerASCIIPrintf(viewer, " %D", sizes[p]+hybsizes[p]);CHKERRQ(ierr);
1134           if (hybsizes[p]   > 0) {ierr = PetscViewerASCIIPrintf(viewer, " (%D)", hybsizes[p]);CHKERRQ(ierr);}
1135           if (ghostsizes[p] > 0) {ierr = PetscViewerASCIIPrintf(viewer, " [%D]", ghostsizes[p]);CHKERRQ(ierr);}
1136         }
1137       }
1138       ierr = PetscViewerASCIIPrintf(viewer, "\n");CHKERRQ(ierr);
1139     }
1140     ierr = PetscFree3(sizes,hybsizes,ghostsizes);CHKERRQ(ierr);
1141     {
1142       const PetscReal      *maxCell;
1143       const PetscReal      *L;
1144       const DMBoundaryType *bd;
1145       PetscBool             per, localized;
1146 
1147       ierr = DMGetPeriodicity(dm, &per, &maxCell, &L, &bd);CHKERRQ(ierr);
1148       ierr = DMGetCoordinatesLocalized(dm, &localized);CHKERRQ(ierr);
1149       if (per) {
1150         ierr = PetscViewerASCIIPrintf(viewer, "Periodic mesh (");CHKERRQ(ierr);
1151         ierr = PetscViewerASCIIUseTabs(viewer, PETSC_FALSE);CHKERRQ(ierr);
1152         for (d = 0; d < dim; ++d) {
1153           if (bd && d > 0) {ierr = PetscViewerASCIIPrintf(viewer, ", ");CHKERRQ(ierr);}
1154           if (bd)    {ierr = PetscViewerASCIIPrintf(viewer, "%s", DMBoundaryTypes[bd[d]]);CHKERRQ(ierr);}
1155         }
1156         ierr = PetscViewerASCIIPrintf(viewer, ") coordinates %s\n", localized ? "localized" : "not localized");CHKERRQ(ierr);
1157         ierr = PetscViewerASCIIUseTabs(viewer, PETSC_TRUE);CHKERRQ(ierr);
1158       }
1159     }
1160     ierr = DMGetNumLabels(dm, &numLabels);CHKERRQ(ierr);
1161     if (numLabels) {ierr = PetscViewerASCIIPrintf(viewer, "Labels:\n");CHKERRQ(ierr);}
1162     for (l = 0; l < numLabels; ++l) {
1163       DMLabel         label;
1164       const char     *name;
1165       IS              valueIS;
1166       const PetscInt *values;
1167       PetscInt        numValues, v;
1168 
1169       ierr = DMGetLabelName(dm, l, &name);CHKERRQ(ierr);
1170       ierr = DMGetLabel(dm, name, &label);CHKERRQ(ierr);
1171       ierr = DMLabelGetNumValues(label, &numValues);CHKERRQ(ierr);
1172       ierr = PetscViewerASCIIPrintf(viewer, "  %s: %D strata with value/size (", name, numValues);CHKERRQ(ierr);
1173       ierr = DMLabelGetValueIS(label, &valueIS);CHKERRQ(ierr);
1174       ierr = ISGetIndices(valueIS, &values);CHKERRQ(ierr);
1175       ierr = PetscViewerASCIIUseTabs(viewer, PETSC_FALSE);CHKERRQ(ierr);
1176       for (v = 0; v < numValues; ++v) {
1177         PetscInt size;
1178 
1179         ierr = DMLabelGetStratumSize(label, values[v], &size);CHKERRQ(ierr);
1180         if (v > 0) {ierr = PetscViewerASCIIPrintf(viewer, ", ");CHKERRQ(ierr);}
1181         ierr = PetscViewerASCIIPrintf(viewer, "%D (%D)", values[v], size);CHKERRQ(ierr);
1182       }
1183       ierr = PetscViewerASCIIPrintf(viewer, ")\n");CHKERRQ(ierr);
1184       ierr = PetscViewerASCIIUseTabs(viewer, PETSC_TRUE);CHKERRQ(ierr);
1185       ierr = ISRestoreIndices(valueIS, &values);CHKERRQ(ierr);
1186       ierr = ISDestroy(&valueIS);CHKERRQ(ierr);
1187     }
1188     /* If no fields are specified, people do not want to see adjacency */
1189     if (dm->Nf) {
1190       PetscInt f;
1191 
1192       for (f = 0; f < dm->Nf; ++f) {
1193         const char *name;
1194 
1195         ierr = PetscObjectGetName(dm->fields[f].disc, &name);CHKERRQ(ierr);
1196         if (numLabels) {ierr = PetscViewerASCIIPrintf(viewer, "Field %s:\n", name);CHKERRQ(ierr);}
1197         ierr = PetscViewerASCIIPushTab(viewer);CHKERRQ(ierr);
1198         if (dm->fields[f].label) {ierr = DMLabelView(dm->fields[f].label, viewer);CHKERRQ(ierr);}
1199         if (dm->fields[f].adjacency[0]) {
1200           if (dm->fields[f].adjacency[1]) {ierr = PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n");CHKERRQ(ierr);}
1201           else                            {ierr = PetscViewerASCIIPrintf(viewer, "adjacency FVM\n");CHKERRQ(ierr);}
1202         } else {
1203           if (dm->fields[f].adjacency[1]) {ierr = PetscViewerASCIIPrintf(viewer, "adjacency FEM\n");CHKERRQ(ierr);}
1204           else                            {ierr = PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n");CHKERRQ(ierr);}
1205         }
1206         ierr = PetscViewerASCIIPopTab(viewer);CHKERRQ(ierr);
1207       }
1208     }
1209     ierr = DMGetCoarseDM(dm, &cdm);CHKERRQ(ierr);
1210     if (cdm) {
1211       ierr = PetscViewerASCIIPushTab(viewer);CHKERRQ(ierr);
1212       ierr = DMPlexView_Ascii(cdm, viewer);CHKERRQ(ierr);
1213       ierr = PetscViewerASCIIPopTab(viewer);CHKERRQ(ierr);
1214     }
1215   }
1216   PetscFunctionReturn(0);
1217 }
1218 
1219 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1220 {
1221   DMPolytopeType ct;
1222   PetscMPIInt    rank;
1223   PetscErrorCode ierr;
1224 
1225   PetscFunctionBegin;
1226   ierr = MPI_Comm_rank(PetscObjectComm((PetscObject) dm), &rank);CHKERRMPI(ierr);
1227   ierr = DMPlexGetCellType(dm, cell, &ct);CHKERRQ(ierr);
1228   switch (ct) {
1229   case DM_POLYTOPE_TRIANGLE:
1230     ierr = PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]),
1231                              PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1232                              PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1233                              PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2);CHKERRQ(ierr);
1234     ierr = PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK);CHKERRQ(ierr);
1235     ierr = PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK);CHKERRQ(ierr);
1236     ierr = PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK);CHKERRQ(ierr);
1237     break;
1238   case DM_POLYTOPE_QUADRILATERAL:
1239     ierr = PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]),
1240                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1241                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1242                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2);CHKERRQ(ierr);
1243     ierr = PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]),
1244                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1245                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1246                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2);CHKERRQ(ierr);
1247     ierr = PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK);CHKERRQ(ierr);
1248     ierr = PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK);CHKERRQ(ierr);
1249     ierr = PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK);CHKERRQ(ierr);
1250     ierr = PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK);CHKERRQ(ierr);
1251     break;
1252   default: SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1253   }
1254   PetscFunctionReturn(0);
1255 }
1256 
1257 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1258 {
1259   DMPolytopeType ct;
1260   PetscReal      centroid[2] = {0., 0.};
1261   PetscMPIInt    rank;
1262   PetscInt       fillColor, v, e, d;
1263   PetscErrorCode ierr;
1264 
1265   PetscFunctionBegin;
1266   ierr = MPI_Comm_rank(PetscObjectComm((PetscObject) dm), &rank);CHKERRMPI(ierr);
1267   ierr = DMPlexGetCellType(dm, cell, &ct);CHKERRQ(ierr);
1268   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2;
1269   switch (ct) {
1270   case DM_POLYTOPE_TRIANGLE:
1271     {
1272       PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1273 
1274       for (v = 0; v < 3; ++v) {centroid[0] += PetscRealPart(coords[v*2+0])/3.;centroid[1] += PetscRealPart(coords[v*2+1])/3.;}
1275       for (e = 0; e < 3; ++e) {
1276         refCoords[0] = refVertices[e*2+0];
1277         refCoords[1] = refVertices[e*2+1];
1278         for (d = 1; d <= edgeDiv; ++d) {
1279           refCoords[d*2+0] = refCoords[0] + (refVertices[(e+1)%3 * 2 + 0] - refCoords[0])*d/edgeDiv;
1280           refCoords[d*2+1] = refCoords[1] + (refVertices[(e+1)%3 * 2 + 1] - refCoords[1])*d/edgeDiv;
1281         }
1282         ierr = DMPlexReferenceToCoordinates(dm, cell, edgeDiv+1, refCoords, edgeCoords);CHKERRQ(ierr);
1283         for (d = 0; d < edgeDiv; ++d) {
1284           ierr = PetscDrawTriangle(draw, centroid[0], centroid[1], edgeCoords[d*2+0], edgeCoords[d*2+1], edgeCoords[(d+1)*2+0], edgeCoords[(d+1)*2+1], fillColor, fillColor, fillColor);CHKERRQ(ierr);
1285           ierr = PetscDrawLine(draw, edgeCoords[d*2+0], edgeCoords[d*2+1], edgeCoords[(d+1)*2+0], edgeCoords[(d+1)*2+1], PETSC_DRAW_BLACK);CHKERRQ(ierr);
1286         }
1287       }
1288     }
1289     break;
1290   default: SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1291   }
1292   PetscFunctionReturn(0);
1293 }
1294 
1295 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1296 {
1297   PetscDraw          draw;
1298   DM                 cdm;
1299   PetscSection       coordSection;
1300   Vec                coordinates;
1301   const PetscScalar *coords;
1302   PetscReal          xyl[2],xyr[2],bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
1303   PetscReal         *refCoords, *edgeCoords;
1304   PetscBool          isnull, drawAffine = PETSC_TRUE;
1305   PetscInt           dim, vStart, vEnd, cStart, cEnd, c, N, edgeDiv = 4;
1306   PetscErrorCode     ierr;
1307 
1308   PetscFunctionBegin;
1309   ierr = DMGetCoordinateDim(dm, &dim);CHKERRQ(ierr);
1310   if (dim != 2) SETERRQ1(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %D", dim);
1311   ierr = PetscOptionsGetBool(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL);CHKERRQ(ierr);
1312   if (!drawAffine) {ierr = PetscMalloc2((edgeDiv+1)*dim, &refCoords, (edgeDiv+1)*dim, &edgeCoords);CHKERRQ(ierr);}
1313   ierr = DMGetCoordinateDM(dm, &cdm);CHKERRQ(ierr);
1314   ierr = DMGetLocalSection(cdm, &coordSection);CHKERRQ(ierr);
1315   ierr = DMGetCoordinatesLocal(dm, &coordinates);CHKERRQ(ierr);
1316   ierr = DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd);CHKERRQ(ierr);
1317   ierr = DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd);CHKERRQ(ierr);
1318 
1319   ierr = PetscViewerDrawGetDraw(viewer, 0, &draw);CHKERRQ(ierr);
1320   ierr = PetscDrawIsNull(draw, &isnull);CHKERRQ(ierr);
1321   if (isnull) PetscFunctionReturn(0);
1322   ierr = PetscDrawSetTitle(draw, "Mesh");CHKERRQ(ierr);
1323 
1324   ierr = VecGetLocalSize(coordinates, &N);CHKERRQ(ierr);
1325   ierr = VecGetArrayRead(coordinates, &coords);CHKERRQ(ierr);
1326   for (c = 0; c < N; c += dim) {
1327     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));   bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
1328     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c+1])); bound[3] = PetscMax(bound[3], PetscRealPart(coords[c+1]));
1329   }
1330   ierr = VecRestoreArrayRead(coordinates, &coords);CHKERRQ(ierr);
1331   ierr = MPIU_Allreduce(&bound[0],xyl,2,MPIU_REAL,MPIU_MIN,PetscObjectComm((PetscObject)dm));CHKERRMPI(ierr);
1332   ierr = MPIU_Allreduce(&bound[2],xyr,2,MPIU_REAL,MPIU_MAX,PetscObjectComm((PetscObject)dm));CHKERRMPI(ierr);
1333   ierr = PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]);CHKERRQ(ierr);
1334   ierr = PetscDrawClear(draw);CHKERRQ(ierr);
1335 
1336   for (c = cStart; c < cEnd; ++c) {
1337     PetscScalar *coords = NULL;
1338     PetscInt     numCoords;
1339 
1340     ierr = DMPlexVecGetClosureAtDepth_Internal(dm, coordSection, coordinates, c, 0, &numCoords, &coords);CHKERRQ(ierr);
1341     if (drawAffine) {
1342       ierr = DMPlexDrawCell(dm, draw, c, coords);CHKERRQ(ierr);
1343     } else {
1344       ierr = DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords);CHKERRQ(ierr);
1345     }
1346     ierr = DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords);CHKERRQ(ierr);
1347   }
1348   if (!drawAffine) {ierr = PetscFree2(refCoords, edgeCoords);CHKERRQ(ierr);}
1349   ierr = PetscDrawFlush(draw);CHKERRQ(ierr);
1350   ierr = PetscDrawPause(draw);CHKERRQ(ierr);
1351   ierr = PetscDrawSave(draw);CHKERRQ(ierr);
1352   PetscFunctionReturn(0);
1353 }
1354 
1355 #if defined(PETSC_HAVE_EXODUSII)
1356 #include <exodusII.h>
1357 #include <petscviewerexodusii.h>
1358 #endif
1359 
1360 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1361 {
1362   PetscBool      iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus;
1363   char           name[PETSC_MAX_PATH_LEN];
1364   PetscErrorCode ierr;
1365 
1366   PetscFunctionBegin;
1367   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1368   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1369   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERASCII,    &iascii);CHKERRQ(ierr);
1370   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,      &isvtk);CHKERRQ(ierr);
1371   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,     &ishdf5);CHKERRQ(ierr);
1372   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERDRAW,     &isdraw);CHKERRQ(ierr);
1373   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERGLVIS,    &isglvis);CHKERRQ(ierr);
1374   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWEREXODUSII, &isexodus);CHKERRQ(ierr);
1375   if (iascii) {
1376     PetscViewerFormat format;
1377     ierr = PetscViewerGetFormat(viewer, &format);CHKERRQ(ierr);
1378     if (format == PETSC_VIEWER_ASCII_GLVIS) {
1379       ierr = DMPlexView_GLVis(dm, viewer);CHKERRQ(ierr);
1380     } else {
1381       ierr = DMPlexView_Ascii(dm, viewer);CHKERRQ(ierr);
1382     }
1383   } else if (ishdf5) {
1384 #if defined(PETSC_HAVE_HDF5)
1385     ierr = DMPlexView_HDF5_Internal(dm, viewer);CHKERRQ(ierr);
1386 #else
1387     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1388 #endif
1389   } else if (isvtk) {
1390     ierr = DMPlexVTKWriteAll((PetscObject) dm,viewer);CHKERRQ(ierr);
1391   } else if (isdraw) {
1392     ierr = DMPlexView_Draw(dm, viewer);CHKERRQ(ierr);
1393   } else if (isglvis) {
1394     ierr = DMPlexView_GLVis(dm, viewer);CHKERRQ(ierr);
1395 #if defined(PETSC_HAVE_EXODUSII)
1396   } else if (isexodus) {
1397 /*
1398       exodusII requires that all sets be part of exactly one cell set.
1399       If the dm does not have a "Cell Sets" label defined, we create one
1400       with ID 1, containig all cells.
1401       Note that if the Cell Sets label is defined but does not cover all cells,
1402       we may still have a problem. This should probably be checked here or in the viewer;
1403     */
1404     PetscInt numCS;
1405     ierr = DMGetLabelSize(dm,"Cell Sets",&numCS);CHKERRQ(ierr);
1406     if (!numCS) {
1407       PetscInt cStart, cEnd, c;
1408       ierr = DMCreateLabel(dm, "Cell Sets");CHKERRQ(ierr);
1409       ierr = DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd);CHKERRQ(ierr);
1410       for (c = cStart; c < cEnd; ++c) {ierr = DMSetLabelValue(dm, "Cell Sets", c, 1);CHKERRQ(ierr);}
1411     }
1412     ierr = DMView_PlexExodusII(dm, viewer);CHKERRQ(ierr);
1413 #endif
1414   } else {
1415     SETERRQ1(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1416   }
1417   /* Optionally view the partition */
1418   ierr = PetscOptionsHasName(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_partition_view", &flg);CHKERRQ(ierr);
1419   if (flg) {
1420     Vec ranks;
1421     ierr = DMPlexCreateRankField(dm, &ranks);CHKERRQ(ierr);
1422     ierr = VecView(ranks, viewer);CHKERRQ(ierr);
1423     ierr = VecDestroy(&ranks);CHKERRQ(ierr);
1424   }
1425   /* Optionally view a label */
1426   ierr = PetscOptionsGetString(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_label_view", name, sizeof(name), &flg);CHKERRQ(ierr);
1427   if (flg) {
1428     DMLabel label;
1429     Vec     val;
1430 
1431     ierr = DMGetLabel(dm, name, &label);CHKERRQ(ierr);
1432     if (!label) SETERRQ1(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1433     ierr = DMPlexCreateLabelField(dm, label, &val);CHKERRQ(ierr);
1434     ierr = VecView(val, viewer);CHKERRQ(ierr);
1435     ierr = VecDestroy(&val);CHKERRQ(ierr);
1436   }
1437   PetscFunctionReturn(0);
1438 }
1439 
1440 /*@
1441   DMPlexTopologyView - Saves a DMPlex topology into a file
1442 
1443   Collective on DM
1444 
1445   Input Parameters:
1446 + dm     - The DM whose topology is to be saved
1447 - viewer - The PetscViewer for saving
1448 
1449   Level: advanced
1450 
1451 .seealso: DMView(), DMPlexCoordinatesView(), DMPlexLabelsView(), DMPlexTopologyLoad()
1452 @*/
1453 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
1454 {
1455   PetscBool      ishdf5;
1456   PetscErrorCode ierr;
1457 
1458   PetscFunctionBegin;
1459   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1460   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1461   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5);CHKERRQ(ierr);
1462   if (ishdf5) {
1463 #if defined(PETSC_HAVE_HDF5)
1464     PetscViewerFormat format;
1465     ierr = PetscViewerGetFormat(viewer, &format);CHKERRQ(ierr);
1466     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1467       IS globalPointNumbering;
1468 
1469       ierr = DMPlexCreatePointNumbering(dm, &globalPointNumbering);CHKERRQ(ierr);
1470       ierr = DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer);CHKERRQ(ierr);
1471       ierr = ISDestroy(&globalPointNumbering);CHKERRQ(ierr);
1472     } else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1473 #else
1474     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1475 #endif
1476   }
1477   PetscFunctionReturn(0);
1478 }
1479 
1480 /*@
1481   DMPlexCoordinatesView - Saves DMPlex coordinates into a file
1482 
1483   Collective on DM
1484 
1485   Input Parameters:
1486 + dm     - The DM whose coordinates are to be saved
1487 - viewer - The PetscViewer for saving
1488 
1489   Level: advanced
1490 
1491 .seealso: DMView(), DMPlexTopologyView(), DMPlexLabelsView(), DMPlexCoordinatesLoad()
1492 @*/
1493 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
1494 {
1495   PetscBool      ishdf5;
1496   PetscErrorCode ierr;
1497 
1498   PetscFunctionBegin;
1499   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1500   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1501   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5);CHKERRQ(ierr);
1502   if (ishdf5) {
1503 #if defined(PETSC_HAVE_HDF5)
1504     PetscViewerFormat format;
1505     ierr = PetscViewerGetFormat(viewer, &format);CHKERRQ(ierr);
1506     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1507       ierr = DMPlexCoordinatesView_HDF5_Internal(dm, viewer);CHKERRQ(ierr);
1508     } else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1509 #else
1510     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1511 #endif
1512   }
1513   PetscFunctionReturn(0);
1514 }
1515 
1516 /*@
1517   DMPlexLabelsView - Saves DMPlex labels into a file
1518 
1519   Collective on DM
1520 
1521   Input Parameters:
1522 + dm     - The DM whose labels are to be saved
1523 - viewer - The PetscViewer for saving
1524 
1525   Level: advanced
1526 
1527 .seealso: DMView(), DMPlexTopologyView(), DMPlexCoordinatesView(), DMPlexLabelsLoad()
1528 @*/
1529 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
1530 {
1531   PetscBool      ishdf5;
1532   PetscErrorCode ierr;
1533 
1534   PetscFunctionBegin;
1535   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1536   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1537   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5);CHKERRQ(ierr);
1538   if (ishdf5) {
1539 #if defined(PETSC_HAVE_HDF5)
1540     IS                globalPointNumbering;
1541     PetscViewerFormat format;
1542 
1543     ierr = PetscViewerGetFormat(viewer, &format);CHKERRQ(ierr);
1544     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1545       ierr = DMPlexCreatePointNumbering(dm, &globalPointNumbering);CHKERRQ(ierr);
1546       ierr = DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer);CHKERRQ(ierr);
1547       ierr = ISDestroy(&globalPointNumbering);CHKERRQ(ierr);
1548     } else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1549 #else
1550     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1551 #endif
1552   }
1553   PetscFunctionReturn(0);
1554 }
1555 
1556 /*@
1557   DMPlexSectionView - Saves a section associated with a DMPlex
1558 
1559   Collective on DM
1560 
1561   Input Parameters:
1562 + dm         - The DM that contains the topology on which the section to be saved is defined
1563 . viewer     - The PetscViewer for saving
1564 - sectiondm  - The DM that contains the section to be saved
1565 
1566   Level: advanced
1567 
1568   Notes:
1569   This function is a wrapper around PetscSectionView(); in addition to the raw section, it saves information that associates the section points to the topology (dm) points. When the topology (dm) and the section are later loaded with DMPlexTopologyLoad() and DMPlexSectionLoad(), respectively, this information is used to match section points with topology points.
1570 
1571   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
1572 
1573 .seealso: DMView(), DMPlexTopologyView(), DMPlexCoordinatesView(), DMPlexLabelsView(), DMPlexGlobalVectorView(), DMPlexLocalVectorView(), PetscSectionView(), DMPlexSectionLoad()
1574 @*/
1575 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
1576 {
1577   PetscBool      ishdf5;
1578   PetscErrorCode ierr;
1579 
1580   PetscFunctionBegin;
1581   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1582   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1583   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1584   ierr = PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5);CHKERRQ(ierr);
1585   if (ishdf5) {
1586 #if defined(PETSC_HAVE_HDF5)
1587     ierr = DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm);CHKERRQ(ierr);
1588 #else
1589     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1590 #endif
1591   }
1592   PetscFunctionReturn(0);
1593 }
1594 
1595 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
1596 {
1597   PetscBool      ishdf5;
1598   PetscErrorCode ierr;
1599 
1600   PetscFunctionBegin;
1601   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1602   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1603   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,   &ishdf5);CHKERRQ(ierr);
1604   if (ishdf5) {
1605 #if defined(PETSC_HAVE_HDF5)
1606     PetscViewerFormat format;
1607     ierr = PetscViewerGetFormat(viewer, &format);CHKERRQ(ierr);
1608     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
1609       ierr = DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer);CHKERRQ(ierr);
1610     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1611       ierr = DMPlexLoad_HDF5_Internal(dm, viewer);CHKERRQ(ierr);
1612     } else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1613     PetscFunctionReturn(0);
1614 #else
1615     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1616 #endif
1617   } else SETERRQ1(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
1618 }
1619 
1620 /*@
1621   DMPlexTopologyLoad - Loads a topology into a DMPlex
1622 
1623   Collective on DM
1624 
1625   Input Parameters:
1626 + dm     - The DM into which the topology is loaded
1627 - viewer - The PetscViewer for the saved topology
1628 
1629   Output Parameters:
1630 . globalToLocalPointSF - The PetscSF that pushes points in [0, N) to the associated points in the loaded plex, where N is the global number of points; NULL if unneeded
1631 
1632   Level: advanced
1633 
1634 .seealso: DMLoad(), DMPlexCoordinatesLoad(), DMPlexLabelsLoad(), DMView(), PetscViewerHDF5Open(), PetscViewerPushFormat()
1635 @*/
1636 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
1637 {
1638   PetscBool      ishdf5;
1639   PetscErrorCode ierr;
1640 
1641   PetscFunctionBegin;
1642   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1643   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1644   if (globalToLocalPointSF) PetscValidPointer(globalToLocalPointSF, 3);
1645   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5);CHKERRQ(ierr);
1646   if (ishdf5) {
1647 #if defined(PETSC_HAVE_HDF5)
1648     PetscViewerFormat format;
1649     ierr = PetscViewerGetFormat(viewer, &format);CHKERRQ(ierr);
1650     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1651       ierr = DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF);CHKERRQ(ierr);
1652     } else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1653 #else
1654     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1655 #endif
1656   }
1657   PetscFunctionReturn(0);
1658 }
1659 
1660 /*@
1661   DMPlexCoordinatesLoad - Loads coordinates into a DMPlex
1662 
1663   Collective on DM
1664 
1665   Input Parameters:
1666 + dm     - The DM into which the coordinates are loaded
1667 - viewer - The PetscViewer for the saved coordinates
1668 
1669   Level: advanced
1670 
1671 .seealso: DMLoad(), DMPlexTopologyLoad(), DMPlexLabelsLoad(), DMView(), PetscViewerHDF5Open(), PetscViewerPushFormat()
1672 @*/
1673 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer)
1674 {
1675   PetscBool      ishdf5;
1676   PetscErrorCode ierr;
1677 
1678   PetscFunctionBegin;
1679   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1680   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1681   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5);CHKERRQ(ierr);
1682   if (ishdf5) {
1683 #if defined(PETSC_HAVE_HDF5)
1684     PetscViewerFormat format;
1685     ierr = PetscViewerGetFormat(viewer, &format);CHKERRQ(ierr);
1686     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1687       ierr = DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer);CHKERRQ(ierr);
1688     } else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1689 #else
1690     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1691 #endif
1692   }
1693   PetscFunctionReturn(0);
1694 }
1695 
1696 /*@
1697   DMPlexLabelsLoad - Loads labels into a DMPlex
1698 
1699   Collective on DM
1700 
1701   Input Parameters:
1702 + dm     - The DM into which the labels are loaded
1703 - viewer - The PetscViewer for the saved labels
1704 
1705   Level: advanced
1706 
1707 .seealso: DMLoad(), DMPlexTopologyLoad(), DMPlexCoordinatesLoad(), DMView(), PetscViewerHDF5Open(), PetscViewerPushFormat()
1708 @*/
1709 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer)
1710 {
1711   PetscBool      ishdf5;
1712   PetscErrorCode ierr;
1713 
1714   PetscFunctionBegin;
1715   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1716   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1717   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5);CHKERRQ(ierr);
1718   if (ishdf5) {
1719 #if defined(PETSC_HAVE_HDF5)
1720     PetscViewerFormat format;
1721 
1722     ierr = PetscViewerGetFormat(viewer, &format);CHKERRQ(ierr);
1723     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1724       ierr = DMPlexLabelsLoad_HDF5_Internal(dm, viewer);CHKERRQ(ierr);
1725     } else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1726 #else
1727     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1728 #endif
1729   }
1730   PetscFunctionReturn(0);
1731 }
1732 
1733 /*@
1734   DMPlexSectionLoad - Loads section into a DMPlex
1735 
1736   Collective on DM
1737 
1738   Input Parameters:
1739 + dm          - The DM that represents the topology
1740 . viewer      - The PetscViewer that represents the on-disk section (sectionA)
1741 . sectiondm   - The DM into which the on-disk section (sectionA) is migrated
1742 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
1743 
1744   Output Parameters
1745 + globalDofSF - The SF that migrates any on-disk Vec data associated with sectionA into a global Vec associated with the sectiondm's global section (NULL if not needed)
1746 - localDofSF  - The SF that migrates any on-disk Vec data associated with sectionA into a local Vec associated with the sectiondm's local section (NULL if not needed)
1747 
1748   Level: advanced
1749 
1750   Notes:
1751   This function is a wrapper around PetscSectionLoad(); it loads, in addition to the raw section, a list of global point numbers that associates each on-disk section point with a global point number in [0, NX), where NX is the number of topology points in dm. Noting that globalToLocalPointSF associates each topology point in dm with a global number in [0, NX), one can readily establish an association of the on-disk section points with the topology points.
1752 
1753   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
1754 
1755   The output parameter, globalDofSF (localDofSF), can later be used with DMPlexGlobalVectorLoad() (DMPlexLocalVectorLoad()) to load on-disk vectors into global (local) vectors associated with sectiondm's global (local) section.
1756 
1757   Example using 2 processes:
1758 $  NX (number of points on dm): 4
1759 $  sectionA                   : the on-disk section
1760 $  vecA                       : a vector associated with sectionA
1761 $  sectionB                   : sectiondm's local section constructed in this function
1762 $  vecB (local)               : a vector associated with sectiondm's local section
1763 $  vecB (global)              : a vector associated with sectiondm's global section
1764 $
1765 $                                     rank 0    rank 1
1766 $  vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
1767 $  sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
1768 $  sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
1769 $  sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
1770 $  [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
1771 $  sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
1772 $  sectionB->atlasDof             :     1 0 1 | 1 3
1773 $  sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
1774 $  vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
1775 $  vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
1776 $
1777 $  where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
1778 
1779 .seealso: DMLoad(), DMPlexTopologyLoad(), DMPlexCoordinatesLoad(), DMPlexLabelsLoad(), DMPlexGlobalVectorLoad(), DMPlexLocalVectorLoad(), PetscSectionLoad(), DMPlexSectionView()
1780 @*/
1781 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
1782 {
1783   PetscBool      ishdf5;
1784   PetscErrorCode ierr;
1785 
1786   PetscFunctionBegin;
1787   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1788   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1789   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1790   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
1791   if (globalDofSF) PetscValidPointer(globalDofSF, 5);
1792   if (localDofSF) PetscValidPointer(localDofSF, 6);
1793   ierr = PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5);CHKERRQ(ierr);
1794   if (ishdf5) {
1795 #if defined(PETSC_HAVE_HDF5)
1796     ierr = DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF);CHKERRQ(ierr);
1797 #else
1798     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1799 #endif
1800   }
1801   PetscFunctionReturn(0);
1802 }
1803 
1804 PetscErrorCode DMDestroy_Plex(DM dm)
1805 {
1806   DM_Plex       *mesh = (DM_Plex*) dm->data;
1807   PetscErrorCode ierr;
1808 
1809   PetscFunctionBegin;
1810   ierr = PetscObjectComposeFunction((PetscObject)dm,"DMSetUpGLVisViewer_C",NULL);CHKERRQ(ierr);
1811   ierr = PetscObjectComposeFunction((PetscObject)dm,"DMPlexInsertBoundaryValues_C", NULL);CHKERRQ(ierr);
1812   ierr = PetscObjectComposeFunction((PetscObject)dm,"DMCreateNeumannOverlap_C", NULL);CHKERRQ(ierr);
1813   ierr = PetscObjectComposeFunction((PetscObject)dm,"DMInterpolateSolution_C", NULL);CHKERRQ(ierr);
1814   if (--mesh->refct > 0) PetscFunctionReturn(0);
1815   ierr = PetscSectionDestroy(&mesh->coneSection);CHKERRQ(ierr);
1816   ierr = PetscFree(mesh->cones);CHKERRQ(ierr);
1817   ierr = PetscFree(mesh->coneOrientations);CHKERRQ(ierr);
1818   ierr = PetscSectionDestroy(&mesh->supportSection);CHKERRQ(ierr);
1819   ierr = PetscSectionDestroy(&mesh->subdomainSection);CHKERRQ(ierr);
1820   ierr = PetscFree(mesh->supports);CHKERRQ(ierr);
1821   ierr = PetscFree(mesh->facesTmp);CHKERRQ(ierr);
1822   ierr = PetscFree(mesh->tetgenOpts);CHKERRQ(ierr);
1823   ierr = PetscFree(mesh->triangleOpts);CHKERRQ(ierr);
1824   ierr = PetscPartitionerDestroy(&mesh->partitioner);CHKERRQ(ierr);
1825   ierr = DMLabelDestroy(&mesh->subpointMap);CHKERRQ(ierr);
1826   ierr = ISDestroy(&mesh->subpointIS);CHKERRQ(ierr);
1827   ierr = ISDestroy(&mesh->globalVertexNumbers);CHKERRQ(ierr);
1828   ierr = ISDestroy(&mesh->globalCellNumbers);CHKERRQ(ierr);
1829   ierr = PetscSectionDestroy(&mesh->anchorSection);CHKERRQ(ierr);
1830   ierr = ISDestroy(&mesh->anchorIS);CHKERRQ(ierr);
1831   ierr = PetscSectionDestroy(&mesh->parentSection);CHKERRQ(ierr);
1832   ierr = PetscFree(mesh->parents);CHKERRQ(ierr);
1833   ierr = PetscFree(mesh->childIDs);CHKERRQ(ierr);
1834   ierr = PetscSectionDestroy(&mesh->childSection);CHKERRQ(ierr);
1835   ierr = PetscFree(mesh->children);CHKERRQ(ierr);
1836   ierr = DMDestroy(&mesh->referenceTree);CHKERRQ(ierr);
1837   ierr = PetscGridHashDestroy(&mesh->lbox);CHKERRQ(ierr);
1838   ierr = PetscFree(mesh->neighbors);CHKERRQ(ierr);
1839   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
1840   ierr = PetscFree(mesh);CHKERRQ(ierr);
1841   PetscFunctionReturn(0);
1842 }
1843 
1844 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
1845 {
1846   PetscSection           sectionGlobal;
1847   PetscInt               bs = -1, mbs;
1848   PetscInt               localSize;
1849   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
1850   PetscErrorCode         ierr;
1851   MatType                mtype;
1852   ISLocalToGlobalMapping ltog;
1853 
1854   PetscFunctionBegin;
1855   ierr = MatInitializePackage();CHKERRQ(ierr);
1856   mtype = dm->mattype;
1857   ierr = DMGetGlobalSection(dm, &sectionGlobal);CHKERRQ(ierr);
1858   /* ierr = PetscSectionGetStorageSize(sectionGlobal, &localSize);CHKERRQ(ierr); */
1859   ierr = PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize);CHKERRQ(ierr);
1860   ierr = MatCreate(PetscObjectComm((PetscObject)dm), J);CHKERRQ(ierr);
1861   ierr = MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE);CHKERRQ(ierr);
1862   ierr = MatSetType(*J, mtype);CHKERRQ(ierr);
1863   ierr = MatSetFromOptions(*J);CHKERRQ(ierr);
1864   ierr = MatGetBlockSize(*J, &mbs);CHKERRQ(ierr);
1865   if (mbs > 1) bs = mbs;
1866   ierr = PetscStrcmp(mtype, MATSHELL, &isShell);CHKERRQ(ierr);
1867   ierr = PetscStrcmp(mtype, MATBAIJ, &isBlock);CHKERRQ(ierr);
1868   ierr = PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock);CHKERRQ(ierr);
1869   ierr = PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock);CHKERRQ(ierr);
1870   ierr = PetscStrcmp(mtype, MATSBAIJ, &isSymBlock);CHKERRQ(ierr);
1871   ierr = PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock);CHKERRQ(ierr);
1872   ierr = PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock);CHKERRQ(ierr);
1873   ierr = PetscStrcmp(mtype, MATIS, &isMatIS);CHKERRQ(ierr);
1874   if (!isShell) {
1875     PetscSection subSection;
1876     PetscBool    fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
1877     PetscInt    *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *ltogidx, lsize;
1878     PetscInt     pStart, pEnd, p, dof, cdof;
1879 
1880     /* Set localtoglobalmapping on the matrix for MatSetValuesLocal() to work (it also creates the local matrices in case of MATIS) */
1881     if (isMatIS) { /* need a different l2g map than the one computed by DMGetLocalToGlobalMapping */
1882       PetscSection section;
1883       PetscInt     size;
1884 
1885       ierr = DMGetLocalSection(dm, &section);CHKERRQ(ierr);
1886       ierr = PetscSectionGetStorageSize(section, &size);CHKERRQ(ierr);
1887       ierr = PetscMalloc1(size,&ltogidx);CHKERRQ(ierr);
1888       ierr = DMPlexGetSubdomainSection(dm, &subSection);CHKERRQ(ierr);
1889     } else {
1890       ierr = DMGetLocalToGlobalMapping(dm,&ltog);CHKERRQ(ierr);
1891     }
1892     ierr = PetscSectionGetChart(sectionGlobal, &pStart, &pEnd);CHKERRQ(ierr);
1893     for (p = pStart, lsize = 0; p < pEnd; ++p) {
1894       PetscInt bdof;
1895 
1896       ierr = PetscSectionGetDof(sectionGlobal, p, &dof);CHKERRQ(ierr);
1897       ierr = PetscSectionGetConstraintDof(sectionGlobal, p, &cdof);CHKERRQ(ierr);
1898       dof  = dof < 0 ? -(dof+1) : dof;
1899       bdof = cdof && (dof-cdof) ? 1 : dof;
1900       if (dof) {
1901         if (bs < 0)          {bs = bdof;}
1902         else if (bs != bdof) {bs = 1; if (!isMatIS) break;}
1903       }
1904       if (isMatIS) {
1905         PetscInt loff,c,off;
1906         ierr = PetscSectionGetOffset(subSection, p, &loff);CHKERRQ(ierr);
1907         ierr = PetscSectionGetOffset(sectionGlobal, p, &off);CHKERRQ(ierr);
1908         for (c = 0; c < dof-cdof; ++c, ++lsize) ltogidx[loff+c] = off > -1 ? off+c : -(off+1)+c;
1909       }
1910     }
1911     /* Must have same blocksize on all procs (some might have no points) */
1912     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs; bsLocal[1] = bs;
1913     ierr = PetscGlobalMinMaxInt(PetscObjectComm((PetscObject) dm), bsLocal, bsMinMax);CHKERRQ(ierr);
1914     if (bsMinMax[0] != bsMinMax[1]) {bs = 1;}
1915     else                            {bs = bsMinMax[0];}
1916     bs = PetscMax(1,bs);
1917     if (isMatIS) { /* Must reduce indices by blocksize */
1918       PetscInt l;
1919 
1920       lsize = lsize/bs;
1921       if (bs > 1) for (l = 0; l < lsize; ++l) ltogidx[l] = ltogidx[l*bs]/bs;
1922       ierr = ISLocalToGlobalMappingCreate(PetscObjectComm((PetscObject)dm), bs, lsize, ltogidx, PETSC_OWN_POINTER, &ltog);CHKERRQ(ierr);
1923     }
1924     ierr = MatSetLocalToGlobalMapping(*J,ltog,ltog);CHKERRQ(ierr);
1925     if (isMatIS) {
1926       ierr = ISLocalToGlobalMappingDestroy(&ltog);CHKERRQ(ierr);
1927     }
1928     ierr = PetscCalloc4(localSize/bs, &dnz, localSize/bs, &onz, localSize/bs, &dnzu, localSize/bs, &onzu);CHKERRQ(ierr);
1929     ierr = DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix);CHKERRQ(ierr);
1930     ierr = PetscFree4(dnz, onz, dnzu, onzu);CHKERRQ(ierr);
1931   }
1932   ierr = MatSetDM(*J, dm);CHKERRQ(ierr);
1933   PetscFunctionReturn(0);
1934 }
1935 
1936 /*@
1937   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
1938 
1939   Not collective
1940 
1941   Input Parameter:
1942 . mesh - The DMPlex
1943 
1944   Output Parameters:
1945 . subsection - The subdomain section
1946 
1947   Level: developer
1948 
1949 .seealso:
1950 @*/
1951 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
1952 {
1953   DM_Plex       *mesh = (DM_Plex*) dm->data;
1954   PetscErrorCode ierr;
1955 
1956   PetscFunctionBegin;
1957   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1958   if (!mesh->subdomainSection) {
1959     PetscSection section;
1960     PetscSF      sf;
1961 
1962     ierr = PetscSFCreate(PETSC_COMM_SELF,&sf);CHKERRQ(ierr);
1963     ierr = DMGetLocalSection(dm,&section);CHKERRQ(ierr);
1964     ierr = PetscSectionCreateGlobalSection(section,sf,PETSC_FALSE,PETSC_TRUE,&mesh->subdomainSection);CHKERRQ(ierr);
1965     ierr = PetscSFDestroy(&sf);CHKERRQ(ierr);
1966   }
1967   *subsection = mesh->subdomainSection;
1968   PetscFunctionReturn(0);
1969 }
1970 
1971 /*@
1972   DMPlexGetChart - Return the interval for all mesh points [pStart, pEnd)
1973 
1974   Not collective
1975 
1976   Input Parameter:
1977 . mesh - The DMPlex
1978 
1979   Output Parameters:
1980 + pStart - The first mesh point
1981 - pEnd   - The upper bound for mesh points
1982 
1983   Level: beginner
1984 
1985 .seealso: DMPlexCreate(), DMPlexSetChart()
1986 @*/
1987 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
1988 {
1989   DM_Plex       *mesh = (DM_Plex*) dm->data;
1990   PetscErrorCode ierr;
1991 
1992   PetscFunctionBegin;
1993   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1994   ierr = PetscSectionGetChart(mesh->coneSection, pStart, pEnd);CHKERRQ(ierr);
1995   PetscFunctionReturn(0);
1996 }
1997 
1998 /*@
1999   DMPlexSetChart - Set the interval for all mesh points [pStart, pEnd)
2000 
2001   Not collective
2002 
2003   Input Parameters:
2004 + mesh - The DMPlex
2005 . pStart - The first mesh point
2006 - pEnd   - The upper bound for mesh points
2007 
2008   Output Parameters:
2009 
2010   Level: beginner
2011 
2012 .seealso: DMPlexCreate(), DMPlexGetChart()
2013 @*/
2014 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2015 {
2016   DM_Plex       *mesh = (DM_Plex*) dm->data;
2017   PetscErrorCode ierr;
2018 
2019   PetscFunctionBegin;
2020   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2021   ierr = PetscSectionSetChart(mesh->coneSection, pStart, pEnd);CHKERRQ(ierr);
2022   ierr = PetscSectionSetChart(mesh->supportSection, pStart, pEnd);CHKERRQ(ierr);
2023   PetscFunctionReturn(0);
2024 }
2025 
2026 /*@
2027   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2028 
2029   Not collective
2030 
2031   Input Parameters:
2032 + mesh - The DMPlex
2033 - p - The point, which must lie in the chart set with DMPlexSetChart()
2034 
2035   Output Parameter:
2036 . size - The cone size for point p
2037 
2038   Level: beginner
2039 
2040 .seealso: DMPlexCreate(), DMPlexSetConeSize(), DMPlexSetChart()
2041 @*/
2042 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2043 {
2044   DM_Plex       *mesh = (DM_Plex*) dm->data;
2045   PetscErrorCode ierr;
2046 
2047   PetscFunctionBegin;
2048   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2049   PetscValidPointer(size, 3);
2050   ierr = PetscSectionGetDof(mesh->coneSection, p, size);CHKERRQ(ierr);
2051   PetscFunctionReturn(0);
2052 }
2053 
2054 /*@
2055   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2056 
2057   Not collective
2058 
2059   Input Parameters:
2060 + mesh - The DMPlex
2061 . p - The point, which must lie in the chart set with DMPlexSetChart()
2062 - size - The cone size for point p
2063 
2064   Output Parameter:
2065 
2066   Note:
2067   This should be called after DMPlexSetChart().
2068 
2069   Level: beginner
2070 
2071 .seealso: DMPlexCreate(), DMPlexGetConeSize(), DMPlexSetChart()
2072 @*/
2073 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
2074 {
2075   DM_Plex       *mesh = (DM_Plex*) dm->data;
2076   PetscErrorCode ierr;
2077 
2078   PetscFunctionBegin;
2079   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2080   ierr = PetscSectionSetDof(mesh->coneSection, p, size);CHKERRQ(ierr);
2081 
2082   mesh->maxConeSize = PetscMax(mesh->maxConeSize, size);
2083   PetscFunctionReturn(0);
2084 }
2085 
2086 /*@
2087   DMPlexAddConeSize - Add the given number of in-edges to this point in the DAG
2088 
2089   Not collective
2090 
2091   Input Parameters:
2092 + mesh - The DMPlex
2093 . p - The point, which must lie in the chart set with DMPlexSetChart()
2094 - size - The additional cone size for point p
2095 
2096   Output Parameter:
2097 
2098   Note:
2099   This should be called after DMPlexSetChart().
2100 
2101   Level: beginner
2102 
2103 .seealso: DMPlexCreate(), DMPlexSetConeSize(), DMPlexGetConeSize(), DMPlexSetChart()
2104 @*/
2105 PetscErrorCode DMPlexAddConeSize(DM dm, PetscInt p, PetscInt size)
2106 {
2107   DM_Plex       *mesh = (DM_Plex*) dm->data;
2108   PetscInt       csize;
2109   PetscErrorCode ierr;
2110 
2111   PetscFunctionBegin;
2112   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2113   ierr = PetscSectionAddDof(mesh->coneSection, p, size);CHKERRQ(ierr);
2114   ierr = PetscSectionGetDof(mesh->coneSection, p, &csize);CHKERRQ(ierr);
2115 
2116   mesh->maxConeSize = PetscMax(mesh->maxConeSize, csize);
2117   PetscFunctionReturn(0);
2118 }
2119 
2120 /*@C
2121   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
2122 
2123   Not collective
2124 
2125   Input Parameters:
2126 + dm - The DMPlex
2127 - p - The point, which must lie in the chart set with DMPlexSetChart()
2128 
2129   Output Parameter:
2130 . cone - An array of points which are on the in-edges for point p
2131 
2132   Level: beginner
2133 
2134   Fortran Notes:
2135   Since it returns an array, this routine is only available in Fortran 90, and you must
2136   include petsc.h90 in your code.
2137   You must also call DMPlexRestoreCone() after you finish using the returned array.
2138   DMPlexRestoreCone() is not needed/available in C.
2139 
2140 .seealso: DMPlexGetConeSize(), DMPlexSetCone(), DMPlexGetConeTuple(), DMPlexSetChart()
2141 @*/
2142 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
2143 {
2144   DM_Plex       *mesh = (DM_Plex*) dm->data;
2145   PetscInt       off;
2146   PetscErrorCode ierr;
2147 
2148   PetscFunctionBegin;
2149   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2150   PetscValidPointer(cone, 3);
2151   ierr  = PetscSectionGetOffset(mesh->coneSection, p, &off);CHKERRQ(ierr);
2152   *cone = &mesh->cones[off];
2153   PetscFunctionReturn(0);
2154 }
2155 
2156 /*@C
2157   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
2158 
2159   Not collective
2160 
2161   Input Parameters:
2162 + dm - The DMPlex
2163 - p - The IS of points, which must lie in the chart set with DMPlexSetChart()
2164 
2165   Output Parameter:
2166 + pConesSection - PetscSection describing the layout of pCones
2167 - pCones - An array of points which are on the in-edges for the point set p
2168 
2169   Level: intermediate
2170 
2171 .seealso: DMPlexCreate(), DMPlexGetCone(), DMPlexGetConeRecursive(), DMPlexSetChart()
2172 @*/
2173 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
2174 {
2175   PetscSection        cs, newcs;
2176   PetscInt            *cones;
2177   PetscInt            *newarr=NULL;
2178   PetscInt            n;
2179   PetscErrorCode      ierr;
2180 
2181   PetscFunctionBegin;
2182   ierr = DMPlexGetCones(dm, &cones);CHKERRQ(ierr);
2183   ierr = DMPlexGetConeSection(dm, &cs);CHKERRQ(ierr);
2184   ierr = PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void**)&newarr) : NULL);CHKERRQ(ierr);
2185   if (pConesSection) *pConesSection = newcs;
2186   if (pCones) {
2187     ierr = PetscSectionGetStorageSize(newcs, &n);CHKERRQ(ierr);
2188     ierr = ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones);CHKERRQ(ierr);
2189   }
2190   PetscFunctionReturn(0);
2191 }
2192 
2193 /*@
2194   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
2195 
2196   Not collective
2197 
2198   Input Parameters:
2199 + dm - The DMPlex
2200 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2201 
2202   Output Parameter:
2203 . expandedPoints - An array of vertices recursively expanded from input points
2204 
2205   Level: advanced
2206 
2207   Notes:
2208   Like DMPlexGetConeRecursive but returns only the 0-depth IS (i.e. vertices only) and no sections.
2209   There is no corresponding Restore function, just call ISDestroy() on the returned IS to deallocate.
2210 
2211 .seealso: DMPlexCreate(), DMPlexGetCone(), DMPlexGetConeTuple(), DMPlexGetConeRecursive(), DMPlexRestoreConeRecursive(), DMPlexGetDepth()
2212 @*/
2213 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
2214 {
2215   IS                  *expandedPointsAll;
2216   PetscInt            depth;
2217   PetscErrorCode      ierr;
2218 
2219   PetscFunctionBegin;
2220   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2221   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2222   PetscValidPointer(expandedPoints, 3);
2223   ierr = DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL);CHKERRQ(ierr);
2224   *expandedPoints = expandedPointsAll[0];
2225   ierr = PetscObjectReference((PetscObject)expandedPointsAll[0]);
2226   ierr = DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL);CHKERRQ(ierr);
2227   PetscFunctionReturn(0);
2228 }
2229 
2230 /*@
2231   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).
2232 
2233   Not collective
2234 
2235   Input Parameters:
2236 + dm - The DMPlex
2237 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2238 
2239   Output Parameter:
2240 + depth - (optional) Size of the output arrays, equal to DMPlex depth, returned by DMPlexGetDepth()
2241 . expandedPoints - (optional) An array of index sets with recursively expanded cones
2242 - sections - (optional) An array of sections which describe mappings from points to their cone points
2243 
2244   Level: advanced
2245 
2246   Notes:
2247   Like DMPlexGetConeTuple() but recursive.
2248 
2249   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.
2250   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
2251 
2252   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:
2253   (1) DAG points in expandedPoints[d+1] with depth d+1 to their cone points in expandedPoints[d];
2254   (2) DAG points in expandedPoints[d+1] with depth in [0,d] to the same points in expandedPoints[d].
2255 
2256 .seealso: DMPlexCreate(), DMPlexGetCone(), DMPlexGetConeTuple(), DMPlexRestoreConeRecursive(), DMPlexGetConeRecursiveVertices(), DMPlexGetDepth()
2257 @*/
2258 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2259 {
2260   const PetscInt      *arr0=NULL, *cone=NULL;
2261   PetscInt            *arr=NULL, *newarr=NULL;
2262   PetscInt            d, depth_, i, n, newn, cn, co, start, end;
2263   IS                  *expandedPoints_;
2264   PetscSection        *sections_;
2265   PetscErrorCode      ierr;
2266 
2267   PetscFunctionBegin;
2268   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2269   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2270   if (depth) PetscValidIntPointer(depth, 3);
2271   if (expandedPoints) PetscValidPointer(expandedPoints, 4);
2272   if (sections) PetscValidPointer(sections, 5);
2273   ierr = ISGetLocalSize(points, &n);CHKERRQ(ierr);
2274   ierr = ISGetIndices(points, &arr0);CHKERRQ(ierr);
2275   ierr = DMPlexGetDepth(dm, &depth_);CHKERRQ(ierr);
2276   ierr = PetscCalloc1(depth_, &expandedPoints_);CHKERRQ(ierr);
2277   ierr = PetscCalloc1(depth_, &sections_);CHKERRQ(ierr);
2278   arr = (PetscInt*) arr0; /* this is ok because first generation of arr is not modified */
2279   for (d=depth_-1; d>=0; d--) {
2280     ierr = PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]);CHKERRQ(ierr);
2281     ierr = PetscSectionSetChart(sections_[d], 0, n);CHKERRQ(ierr);
2282     for (i=0; i<n; i++) {
2283       ierr = DMPlexGetDepthStratum(dm, d+1, &start, &end);CHKERRQ(ierr);
2284       if (arr[i] >= start && arr[i] < end) {
2285         ierr = DMPlexGetConeSize(dm, arr[i], &cn);CHKERRQ(ierr);
2286         ierr = PetscSectionSetDof(sections_[d], i, cn);CHKERRQ(ierr);
2287       } else {
2288         ierr = PetscSectionSetDof(sections_[d], i, 1);CHKERRQ(ierr);
2289       }
2290     }
2291     ierr = PetscSectionSetUp(sections_[d]);CHKERRQ(ierr);
2292     ierr = PetscSectionGetStorageSize(sections_[d], &newn);CHKERRQ(ierr);
2293     ierr = PetscMalloc1(newn, &newarr);CHKERRQ(ierr);
2294     for (i=0; i<n; i++) {
2295       ierr = PetscSectionGetDof(sections_[d], i, &cn);CHKERRQ(ierr);
2296       ierr = PetscSectionGetOffset(sections_[d], i, &co);CHKERRQ(ierr);
2297       if (cn > 1) {
2298         ierr = DMPlexGetCone(dm, arr[i], &cone);CHKERRQ(ierr);
2299         ierr = PetscMemcpy(&newarr[co], cone, cn*sizeof(PetscInt));CHKERRQ(ierr);
2300       } else {
2301         newarr[co] = arr[i];
2302       }
2303     }
2304     ierr = ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]);CHKERRQ(ierr);
2305     arr = newarr;
2306     n = newn;
2307   }
2308   ierr = ISRestoreIndices(points, &arr0);CHKERRQ(ierr);
2309   *depth = depth_;
2310   if (expandedPoints) *expandedPoints = expandedPoints_;
2311   else {
2312     for (d=0; d<depth_; d++) {ierr = ISDestroy(&expandedPoints_[d]);CHKERRQ(ierr);}
2313     ierr = PetscFree(expandedPoints_);CHKERRQ(ierr);
2314   }
2315   if (sections) *sections = sections_;
2316   else {
2317     for (d=0; d<depth_; d++) {ierr = PetscSectionDestroy(&sections_[d]);CHKERRQ(ierr);}
2318     ierr = PetscFree(sections_);CHKERRQ(ierr);
2319   }
2320   PetscFunctionReturn(0);
2321 }
2322 
2323 /*@
2324   DMPlexRestoreConeRecursive - Deallocates arrays created by DMPlexGetConeRecursive
2325 
2326   Not collective
2327 
2328   Input Parameters:
2329 + dm - The DMPlex
2330 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2331 
2332   Output Parameter:
2333 + depth - (optional) Size of the output arrays, equal to DMPlex depth, returned by DMPlexGetDepth()
2334 . expandedPoints - (optional) An array of recursively expanded cones
2335 - sections - (optional) An array of sections which describe mappings from points to their cone points
2336 
2337   Level: advanced
2338 
2339   Notes:
2340   See DMPlexGetConeRecursive() for details.
2341 
2342 .seealso: DMPlexCreate(), DMPlexGetCone(), DMPlexGetConeTuple(), DMPlexGetConeRecursive(), DMPlexGetConeRecursiveVertices(), DMPlexGetDepth()
2343 @*/
2344 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2345 {
2346   PetscInt            d, depth_;
2347   PetscErrorCode      ierr;
2348 
2349   PetscFunctionBegin;
2350   ierr = DMPlexGetDepth(dm, &depth_);CHKERRQ(ierr);
2351   if (depth && *depth != depth_) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
2352   if (depth) *depth = 0;
2353   if (expandedPoints) {
2354     for (d=0; d<depth_; d++) {ierr = ISDestroy(&((*expandedPoints)[d]));CHKERRQ(ierr);}
2355     ierr = PetscFree(*expandedPoints);CHKERRQ(ierr);
2356   }
2357   if (sections)  {
2358     for (d=0; d<depth_; d++) {ierr = PetscSectionDestroy(&((*sections)[d]));CHKERRQ(ierr);}
2359     ierr = PetscFree(*sections);CHKERRQ(ierr);
2360   }
2361   PetscFunctionReturn(0);
2362 }
2363 
2364 /*@
2365   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
2366 
2367   Not collective
2368 
2369   Input Parameters:
2370 + mesh - The DMPlex
2371 . p - The point, which must lie in the chart set with DMPlexSetChart()
2372 - cone - An array of points which are on the in-edges for point p
2373 
2374   Output Parameter:
2375 
2376   Note:
2377   This should be called after all calls to DMPlexSetConeSize() and DMSetUp().
2378 
2379   Developer Note: Why not call this DMPlexSetCover()
2380 
2381   Level: beginner
2382 
2383 .seealso: DMPlexCreate(), DMPlexGetCone(), DMPlexSetChart(), DMPlexSetConeSize(), DMSetUp(), DMPlexSetSupport(), DMPlexSetSupportSize()
2384 @*/
2385 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
2386 {
2387   DM_Plex       *mesh = (DM_Plex*) dm->data;
2388   PetscInt       pStart, pEnd;
2389   PetscInt       dof, off, c;
2390   PetscErrorCode ierr;
2391 
2392   PetscFunctionBegin;
2393   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2394   ierr = PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd);CHKERRQ(ierr);
2395   ierr = PetscSectionGetDof(mesh->coneSection, p, &dof);CHKERRQ(ierr);
2396   if (dof) PetscValidPointer(cone, 3);
2397   ierr = PetscSectionGetOffset(mesh->coneSection, p, &off);CHKERRQ(ierr);
2398   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);
2399   for (c = 0; c < dof; ++c) {
2400     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);
2401     mesh->cones[off+c] = cone[c];
2402   }
2403   PetscFunctionReturn(0);
2404 }
2405 
2406 /*@C
2407   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
2408 
2409   Not collective
2410 
2411   Input Parameters:
2412 + mesh - The DMPlex
2413 - p - The point, which must lie in the chart set with DMPlexSetChart()
2414 
2415   Output Parameter:
2416 . coneOrientation - An array of orientations which are on the in-edges for point p. An orientation is an
2417                     integer giving the prescription for cone traversal. If it is negative, the cone is
2418                     traversed in the opposite direction. Its value 'o', or if negative '-(o+1)', gives
2419                     the index of the cone point on which to start.
2420 
2421   Level: beginner
2422 
2423   Fortran Notes:
2424   Since it returns an array, this routine is only available in Fortran 90, and you must
2425   include petsc.h90 in your code.
2426   You must also call DMPlexRestoreConeOrientation() after you finish using the returned array.
2427   DMPlexRestoreConeOrientation() is not needed/available in C.
2428 
2429 .seealso: DMPlexCreate(), DMPlexGetCone(), DMPlexSetCone(), DMPlexSetChart()
2430 @*/
2431 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
2432 {
2433   DM_Plex       *mesh = (DM_Plex*) dm->data;
2434   PetscInt       off;
2435   PetscErrorCode ierr;
2436 
2437   PetscFunctionBegin;
2438   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2439   if (PetscDefined(USE_DEBUG)) {
2440     PetscInt dof;
2441     ierr = PetscSectionGetDof(mesh->coneSection, p, &dof);CHKERRQ(ierr);
2442     if (dof) PetscValidPointer(coneOrientation, 3);
2443   }
2444   ierr = PetscSectionGetOffset(mesh->coneSection, p, &off);CHKERRQ(ierr);
2445 
2446   *coneOrientation = &mesh->coneOrientations[off];
2447   PetscFunctionReturn(0);
2448 }
2449 
2450 /*@
2451   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
2452 
2453   Not collective
2454 
2455   Input Parameters:
2456 + mesh - The DMPlex
2457 . p - The point, which must lie in the chart set with DMPlexSetChart()
2458 - coneOrientation - An array of orientations which are on the in-edges for point p. An orientation is an
2459                     integer giving the prescription for cone traversal. If it is negative, the cone is
2460                     traversed in the opposite direction. Its value 'o', or if negative '-(o+1)', gives
2461                     the index of the cone point on which to start.
2462 
2463   Output Parameter:
2464 
2465   Note:
2466   This should be called after all calls to DMPlexSetConeSize() and DMSetUp().
2467 
2468   Level: beginner
2469 
2470 .seealso: DMPlexCreate(), DMPlexGetConeOrientation(), DMPlexSetCone(), DMPlexSetChart(), DMPlexSetConeSize(), DMSetUp()
2471 @*/
2472 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
2473 {
2474   DM_Plex       *mesh = (DM_Plex*) dm->data;
2475   PetscInt       pStart, pEnd;
2476   PetscInt       dof, off, c;
2477   PetscErrorCode ierr;
2478 
2479   PetscFunctionBegin;
2480   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2481   ierr = PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd);CHKERRQ(ierr);
2482   ierr = PetscSectionGetDof(mesh->coneSection, p, &dof);CHKERRQ(ierr);
2483   if (dof) PetscValidPointer(coneOrientation, 3);
2484   ierr = PetscSectionGetOffset(mesh->coneSection, p, &off);CHKERRQ(ierr);
2485   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);
2486   for (c = 0; c < dof; ++c) {
2487     PetscInt cdof, o = coneOrientation[c];
2488 
2489     ierr = PetscSectionGetDof(mesh->coneSection, mesh->cones[off+c], &cdof);CHKERRQ(ierr);
2490     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);
2491     mesh->coneOrientations[off+c] = o;
2492   }
2493   PetscFunctionReturn(0);
2494 }
2495 
2496 /*@
2497   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
2498 
2499   Not collective
2500 
2501   Input Parameters:
2502 + mesh - The DMPlex
2503 . p - The point, which must lie in the chart set with DMPlexSetChart()
2504 . conePos - The local index in the cone where the point should be put
2505 - conePoint - The mesh point to insert
2506 
2507   Level: beginner
2508 
2509 .seealso: DMPlexCreate(), DMPlexGetCone(), DMPlexSetChart(), DMPlexSetConeSize(), DMSetUp()
2510 @*/
2511 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
2512 {
2513   DM_Plex       *mesh = (DM_Plex*) dm->data;
2514   PetscInt       pStart, pEnd;
2515   PetscInt       dof, off;
2516   PetscErrorCode ierr;
2517 
2518   PetscFunctionBegin;
2519   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2520   ierr = PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd);CHKERRQ(ierr);
2521   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);
2522   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);
2523   ierr = PetscSectionGetDof(mesh->coneSection, p, &dof);CHKERRQ(ierr);
2524   ierr = PetscSectionGetOffset(mesh->coneSection, p, &off);CHKERRQ(ierr);
2525   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);
2526   mesh->cones[off+conePos] = conePoint;
2527   PetscFunctionReturn(0);
2528 }
2529 
2530 /*@
2531   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
2532 
2533   Not collective
2534 
2535   Input Parameters:
2536 + mesh - The DMPlex
2537 . p - The point, which must lie in the chart set with DMPlexSetChart()
2538 . conePos - The local index in the cone where the point should be put
2539 - coneOrientation - The point orientation to insert
2540 
2541   Level: beginner
2542 
2543 .seealso: DMPlexCreate(), DMPlexGetCone(), DMPlexSetChart(), DMPlexSetConeSize(), DMSetUp()
2544 @*/
2545 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
2546 {
2547   DM_Plex       *mesh = (DM_Plex*) dm->data;
2548   PetscInt       pStart, pEnd;
2549   PetscInt       dof, off;
2550   PetscErrorCode ierr;
2551 
2552   PetscFunctionBegin;
2553   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2554   ierr = PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd);CHKERRQ(ierr);
2555   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);
2556   ierr = PetscSectionGetDof(mesh->coneSection, p, &dof);CHKERRQ(ierr);
2557   ierr = PetscSectionGetOffset(mesh->coneSection, p, &off);CHKERRQ(ierr);
2558   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);
2559   mesh->coneOrientations[off+conePos] = coneOrientation;
2560   PetscFunctionReturn(0);
2561 }
2562 
2563 /*@
2564   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
2565 
2566   Not collective
2567 
2568   Input Parameters:
2569 + mesh - The DMPlex
2570 - p - The point, which must lie in the chart set with DMPlexSetChart()
2571 
2572   Output Parameter:
2573 . size - The support size for point p
2574 
2575   Level: beginner
2576 
2577 .seealso: DMPlexCreate(), DMPlexSetConeSize(), DMPlexSetChart(), DMPlexGetConeSize()
2578 @*/
2579 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
2580 {
2581   DM_Plex       *mesh = (DM_Plex*) dm->data;
2582   PetscErrorCode ierr;
2583 
2584   PetscFunctionBegin;
2585   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2586   PetscValidPointer(size, 3);
2587   ierr = PetscSectionGetDof(mesh->supportSection, p, size);CHKERRQ(ierr);
2588   PetscFunctionReturn(0);
2589 }
2590 
2591 /*@
2592   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
2593 
2594   Not collective
2595 
2596   Input Parameters:
2597 + mesh - The DMPlex
2598 . p - The point, which must lie in the chart set with DMPlexSetChart()
2599 - size - The support size for point p
2600 
2601   Output Parameter:
2602 
2603   Note:
2604   This should be called after DMPlexSetChart().
2605 
2606   Level: beginner
2607 
2608 .seealso: DMPlexCreate(), DMPlexGetSupportSize(), DMPlexSetChart()
2609 @*/
2610 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
2611 {
2612   DM_Plex       *mesh = (DM_Plex*) dm->data;
2613   PetscErrorCode ierr;
2614 
2615   PetscFunctionBegin;
2616   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2617   ierr = PetscSectionSetDof(mesh->supportSection, p, size);CHKERRQ(ierr);
2618 
2619   mesh->maxSupportSize = PetscMax(mesh->maxSupportSize, size);
2620   PetscFunctionReturn(0);
2621 }
2622 
2623 /*@C
2624   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
2625 
2626   Not collective
2627 
2628   Input Parameters:
2629 + mesh - The DMPlex
2630 - p - The point, which must lie in the chart set with DMPlexSetChart()
2631 
2632   Output Parameter:
2633 . support - An array of points which are on the out-edges for point p
2634 
2635   Level: beginner
2636 
2637   Fortran Notes:
2638   Since it returns an array, this routine is only available in Fortran 90, and you must
2639   include petsc.h90 in your code.
2640   You must also call DMPlexRestoreSupport() after you finish using the returned array.
2641   DMPlexRestoreSupport() is not needed/available in C.
2642 
2643 .seealso: DMPlexGetSupportSize(), DMPlexSetSupport(), DMPlexGetCone(), DMPlexSetChart()
2644 @*/
2645 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
2646 {
2647   DM_Plex       *mesh = (DM_Plex*) dm->data;
2648   PetscInt       off;
2649   PetscErrorCode ierr;
2650 
2651   PetscFunctionBegin;
2652   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2653   PetscValidPointer(support, 3);
2654   ierr     = PetscSectionGetOffset(mesh->supportSection, p, &off);CHKERRQ(ierr);
2655   *support = &mesh->supports[off];
2656   PetscFunctionReturn(0);
2657 }
2658 
2659 /*@
2660   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
2661 
2662   Not collective
2663 
2664   Input Parameters:
2665 + mesh - The DMPlex
2666 . p - The point, which must lie in the chart set with DMPlexSetChart()
2667 - support - An array of points which are on the out-edges for point p
2668 
2669   Output Parameter:
2670 
2671   Note:
2672   This should be called after all calls to DMPlexSetSupportSize() and DMSetUp().
2673 
2674   Level: beginner
2675 
2676 .seealso: DMPlexSetCone(), DMPlexSetConeSize(), DMPlexCreate(), DMPlexGetSupport(), DMPlexSetChart(), DMPlexSetSupportSize(), DMSetUp()
2677 @*/
2678 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
2679 {
2680   DM_Plex       *mesh = (DM_Plex*) dm->data;
2681   PetscInt       pStart, pEnd;
2682   PetscInt       dof, off, c;
2683   PetscErrorCode ierr;
2684 
2685   PetscFunctionBegin;
2686   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2687   ierr = PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd);CHKERRQ(ierr);
2688   ierr = PetscSectionGetDof(mesh->supportSection, p, &dof);CHKERRQ(ierr);
2689   if (dof) PetscValidPointer(support, 3);
2690   ierr = PetscSectionGetOffset(mesh->supportSection, p, &off);CHKERRQ(ierr);
2691   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);
2692   for (c = 0; c < dof; ++c) {
2693     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);
2694     mesh->supports[off+c] = support[c];
2695   }
2696   PetscFunctionReturn(0);
2697 }
2698 
2699 /*@
2700   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
2701 
2702   Not collective
2703 
2704   Input Parameters:
2705 + mesh - The DMPlex
2706 . p - The point, which must lie in the chart set with DMPlexSetChart()
2707 . supportPos - The local index in the cone where the point should be put
2708 - supportPoint - The mesh point to insert
2709 
2710   Level: beginner
2711 
2712 .seealso: DMPlexCreate(), DMPlexGetCone(), DMPlexSetChart(), DMPlexSetConeSize(), DMSetUp()
2713 @*/
2714 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
2715 {
2716   DM_Plex       *mesh = (DM_Plex*) dm->data;
2717   PetscInt       pStart, pEnd;
2718   PetscInt       dof, off;
2719   PetscErrorCode ierr;
2720 
2721   PetscFunctionBegin;
2722   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2723   ierr = PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd);CHKERRQ(ierr);
2724   ierr = PetscSectionGetDof(mesh->supportSection, p, &dof);CHKERRQ(ierr);
2725   ierr = PetscSectionGetOffset(mesh->supportSection, p, &off);CHKERRQ(ierr);
2726   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);
2727   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);
2728   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);
2729   mesh->supports[off+supportPos] = supportPoint;
2730   PetscFunctionReturn(0);
2731 }
2732 
2733 /*@C
2734   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
2735 
2736   Not collective
2737 
2738   Input Parameters:
2739 + mesh - The DMPlex
2740 . p - The point, which must lie in the chart set with DMPlexSetChart()
2741 . useCone - PETSC_TRUE for in-edges,  otherwise use out-edges
2742 - points - If points is NULL on input, internal storage will be returned, otherwise the provided array is used
2743 
2744   Output Parameters:
2745 + numPoints - The number of points in the closure, so points[] is of size 2*numPoints
2746 - points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
2747 
2748   Note:
2749   If using internal storage (points is NULL on input), each call overwrites the last output.
2750 
2751   Fortran Notes:
2752   Since it returns an array, this routine is only available in Fortran 90, and you must
2753   include petsc.h90 in your code.
2754 
2755   The numPoints argument is not present in the Fortran 90 binding since it is internal to the array.
2756 
2757   Level: beginner
2758 
2759 .seealso: DMPlexRestoreTransitiveClosure(), DMPlexCreate(), DMPlexSetCone(), DMPlexSetChart(), DMPlexGetCone()
2760 @*/
2761 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
2762 {
2763   DM_Plex        *mesh = (DM_Plex*) dm->data;
2764   PetscInt       *closure, *fifo;
2765   const PetscInt *tmp = NULL, *tmpO = NULL;
2766   PetscInt        tmpSize, t;
2767   PetscInt        depth       = 0, maxSize;
2768   PetscInt        closureSize = 2, fifoSize = 0, fifoStart = 0;
2769   PetscErrorCode  ierr;
2770 
2771   PetscFunctionBegin;
2772   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2773   ierr    = DMPlexGetDepth(dm, &depth);CHKERRQ(ierr);
2774   /* This is only 1-level */
2775   if (useCone) {
2776     ierr = DMPlexGetConeSize(dm, p, &tmpSize);CHKERRQ(ierr);
2777     ierr = DMPlexGetCone(dm, p, &tmp);CHKERRQ(ierr);
2778     ierr = DMPlexGetConeOrientation(dm, p, &tmpO);CHKERRQ(ierr);
2779   } else {
2780     ierr = DMPlexGetSupportSize(dm, p, &tmpSize);CHKERRQ(ierr);
2781     ierr = DMPlexGetSupport(dm, p, &tmp);CHKERRQ(ierr);
2782   }
2783   if (depth == 1) {
2784     if (*points) {
2785       closure = *points;
2786     } else {
2787       maxSize = 2*(PetscMax(mesh->maxConeSize, mesh->maxSupportSize)+1);
2788       ierr = DMGetWorkArray(dm, maxSize, MPIU_INT, &closure);CHKERRQ(ierr);
2789     }
2790     closure[0] = p; closure[1] = 0;
2791     for (t = 0; t < tmpSize; ++t, closureSize += 2) {
2792       closure[closureSize]   = tmp[t];
2793       closure[closureSize+1] = tmpO ? tmpO[t] : 0;
2794     }
2795     if (numPoints) *numPoints = closureSize/2;
2796     if (points)    *points    = closure;
2797     PetscFunctionReturn(0);
2798   }
2799   {
2800     PetscInt c, coneSeries, s,supportSeries;
2801 
2802     c = mesh->maxConeSize;
2803     coneSeries = (c > 1) ? ((PetscPowInt(c,depth+1)-1)/(c-1)) : depth+1;
2804     s = mesh->maxSupportSize;
2805     supportSeries = (s > 1) ? ((PetscPowInt(s,depth+1)-1)/(s-1)) : depth+1;
2806     maxSize = 2*PetscMax(coneSeries,supportSeries);
2807   }
2808   ierr    = DMGetWorkArray(dm, maxSize, MPIU_INT, &fifo);CHKERRQ(ierr);
2809   if (*points) {
2810     closure = *points;
2811   } else {
2812     ierr = DMGetWorkArray(dm, maxSize, MPIU_INT, &closure);CHKERRQ(ierr);
2813   }
2814   closure[0] = p; closure[1] = 0;
2815   for (t = 0; t < tmpSize; ++t, closureSize += 2, fifoSize += 2) {
2816     const PetscInt cp = tmp[t];
2817     const PetscInt co = tmpO ? tmpO[t] : 0;
2818 
2819     closure[closureSize]   = cp;
2820     closure[closureSize+1] = co;
2821     fifo[fifoSize]         = cp;
2822     fifo[fifoSize+1]       = co;
2823   }
2824   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
2825   while (fifoSize - fifoStart) {
2826     const PetscInt q   = fifo[fifoStart];
2827     const PetscInt o   = fifo[fifoStart+1];
2828     const PetscInt rev = o >= 0 ? 0 : 1;
2829     const PetscInt off = rev ? -(o+1) : o;
2830 
2831     if (useCone) {
2832       ierr = DMPlexGetConeSize(dm, q, &tmpSize);CHKERRQ(ierr);
2833       ierr = DMPlexGetCone(dm, q, &tmp);CHKERRQ(ierr);
2834       ierr = DMPlexGetConeOrientation(dm, q, &tmpO);CHKERRQ(ierr);
2835     } else {
2836       ierr = DMPlexGetSupportSize(dm, q, &tmpSize);CHKERRQ(ierr);
2837       ierr = DMPlexGetSupport(dm, q, &tmp);CHKERRQ(ierr);
2838       tmpO = NULL;
2839     }
2840     for (t = 0; t < tmpSize; ++t) {
2841       const PetscInt i  = ((rev ? tmpSize-t : t) + off)%tmpSize;
2842       const PetscInt cp = tmp[i];
2843       /* Must propogate orientation: When we reverse orientation, we both reverse the direction of iteration and start at the other end of the chain. */
2844       /* HACK: It is worse to get the size here, than to change the interpretation of -(*+1)
2845        const PetscInt co = tmpO ? (rev ? -(tmpO[i]+1) : tmpO[i]) : 0; */
2846       PetscInt       co = tmpO ? tmpO[i] : 0;
2847       PetscInt       c;
2848 
2849       if (rev) {
2850         PetscInt childSize, coff;
2851         ierr = DMPlexGetConeSize(dm, cp, &childSize);CHKERRQ(ierr);
2852         coff = tmpO[i] < 0 ? -(tmpO[i]+1) : tmpO[i];
2853         co   = childSize ? -(((coff+childSize-1)%childSize)+1) : 0;
2854       }
2855       /* Check for duplicate */
2856       for (c = 0; c < closureSize; c += 2) {
2857         if (closure[c] == cp) break;
2858       }
2859       if (c == closureSize) {
2860         closure[closureSize]   = cp;
2861         closure[closureSize+1] = co;
2862         fifo[fifoSize]         = cp;
2863         fifo[fifoSize+1]       = co;
2864         closureSize           += 2;
2865         fifoSize              += 2;
2866       }
2867     }
2868     fifoStart += 2;
2869   }
2870   if (numPoints) *numPoints = closureSize/2;
2871   if (points)    *points    = closure;
2872   ierr = DMRestoreWorkArray(dm, maxSize, MPIU_INT, &fifo);CHKERRQ(ierr);
2873   PetscFunctionReturn(0);
2874 }
2875 
2876 /*@C
2877   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
2878 
2879   Not collective
2880 
2881   Input Parameters:
2882 + mesh - The DMPlex
2883 . p - The point, which must lie in the chart set with DMPlexSetChart()
2884 . orientation - The orientation of the point
2885 . useCone - PETSC_TRUE for in-edges,  otherwise use out-edges
2886 - points - If points is NULL on input, internal storage will be returned, otherwise the provided array is used
2887 
2888   Output Parameters:
2889 + numPoints - The number of points in the closure, so points[] is of size 2*numPoints
2890 - points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
2891 
2892   Note:
2893   If using internal storage (points is NULL on input), each call overwrites the last output.
2894 
2895   Fortran Notes:
2896   Since it returns an array, this routine is only available in Fortran 90, and you must
2897   include petsc.h90 in your code.
2898 
2899   The numPoints argument is not present in the Fortran 90 binding since it is internal to the array.
2900 
2901   Level: beginner
2902 
2903 .seealso: DMPlexRestoreTransitiveClosure(), DMPlexCreate(), DMPlexSetCone(), DMPlexSetChart(), DMPlexGetCone()
2904 @*/
2905 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
2906 {
2907   DM_Plex        *mesh = (DM_Plex*) dm->data;
2908   PetscInt       *closure, *fifo;
2909   const PetscInt *tmp = NULL, *tmpO = NULL;
2910   PetscInt        tmpSize, t;
2911   PetscInt        depth       = 0, maxSize;
2912   PetscInt        closureSize = 2, fifoSize = 0, fifoStart = 0;
2913   PetscErrorCode  ierr;
2914 
2915   PetscFunctionBegin;
2916   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2917   ierr    = DMPlexGetDepth(dm, &depth);CHKERRQ(ierr);
2918   /* This is only 1-level */
2919   if (useCone) {
2920     ierr = DMPlexGetConeSize(dm, p, &tmpSize);CHKERRQ(ierr);
2921     ierr = DMPlexGetCone(dm, p, &tmp);CHKERRQ(ierr);
2922     ierr = DMPlexGetConeOrientation(dm, p, &tmpO);CHKERRQ(ierr);
2923   } else {
2924     ierr = DMPlexGetSupportSize(dm, p, &tmpSize);CHKERRQ(ierr);
2925     ierr = DMPlexGetSupport(dm, p, &tmp);CHKERRQ(ierr);
2926   }
2927   if (depth == 1) {
2928     if (*points) {
2929       closure = *points;
2930     } else {
2931       maxSize = 2*(PetscMax(mesh->maxConeSize, mesh->maxSupportSize)+1);
2932       ierr = DMGetWorkArray(dm, maxSize, MPIU_INT, &closure);CHKERRQ(ierr);
2933     }
2934     closure[0] = p; closure[1] = ornt;
2935     for (t = 0; t < tmpSize; ++t, closureSize += 2) {
2936       const PetscInt i = ornt >= 0 ? (t+ornt)%tmpSize : (-(ornt+1) + tmpSize-t)%tmpSize;
2937       closure[closureSize]   = tmp[i];
2938       closure[closureSize+1] = tmpO ? tmpO[i] : 0;
2939     }
2940     if (numPoints) *numPoints = closureSize/2;
2941     if (points)    *points    = closure;
2942     PetscFunctionReturn(0);
2943   }
2944   {
2945     PetscInt c, coneSeries, s,supportSeries;
2946 
2947     c = mesh->maxConeSize;
2948     coneSeries = (c > 1) ? ((PetscPowInt(c,depth+1)-1)/(c-1)) : depth+1;
2949     s = mesh->maxSupportSize;
2950     supportSeries = (s > 1) ? ((PetscPowInt(s,depth+1)-1)/(s-1)) : depth+1;
2951     maxSize = 2*PetscMax(coneSeries,supportSeries);
2952   }
2953   ierr    = DMGetWorkArray(dm, maxSize, MPIU_INT, &fifo);CHKERRQ(ierr);
2954   if (*points) {
2955     closure = *points;
2956   } else {
2957     ierr = DMGetWorkArray(dm, maxSize, MPIU_INT, &closure);CHKERRQ(ierr);
2958   }
2959   closure[0] = p; closure[1] = ornt;
2960   for (t = 0; t < tmpSize; ++t, closureSize += 2, fifoSize += 2) {
2961     const PetscInt i  = ornt >= 0 ? (t+ornt)%tmpSize : (-(ornt+1) + tmpSize-t)%tmpSize;
2962     const PetscInt cp = tmp[i];
2963     PetscInt       co = tmpO ? tmpO[i] : 0;
2964 
2965     if (ornt < 0) {
2966       PetscInt childSize, coff;
2967       ierr = DMPlexGetConeSize(dm, cp, &childSize);CHKERRQ(ierr);
2968       coff = co < 0 ? -(tmpO[i]+1) : tmpO[i];
2969       co   = childSize ? -(((coff+childSize-1)%childSize)+1) : 0;
2970     }
2971     closure[closureSize]   = cp;
2972     closure[closureSize+1] = co;
2973     fifo[fifoSize]         = cp;
2974     fifo[fifoSize+1]       = co;
2975   }
2976   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
2977   while (fifoSize - fifoStart) {
2978     const PetscInt q   = fifo[fifoStart];
2979     const PetscInt o   = fifo[fifoStart+1];
2980     const PetscInt rev = o >= 0 ? 0 : 1;
2981     const PetscInt off = rev ? -(o+1) : o;
2982 
2983     if (useCone) {
2984       ierr = DMPlexGetConeSize(dm, q, &tmpSize);CHKERRQ(ierr);
2985       ierr = DMPlexGetCone(dm, q, &tmp);CHKERRQ(ierr);
2986       ierr = DMPlexGetConeOrientation(dm, q, &tmpO);CHKERRQ(ierr);
2987     } else {
2988       ierr = DMPlexGetSupportSize(dm, q, &tmpSize);CHKERRQ(ierr);
2989       ierr = DMPlexGetSupport(dm, q, &tmp);CHKERRQ(ierr);
2990       tmpO = NULL;
2991     }
2992     for (t = 0; t < tmpSize; ++t) {
2993       const PetscInt i  = ((rev ? tmpSize-t : t) + off)%tmpSize;
2994       const PetscInt cp = tmp[i];
2995       /* Must propogate orientation: When we reverse orientation, we both reverse the direction of iteration and start at the other end of the chain. */
2996       /* HACK: It is worse to get the size here, than to change the interpretation of -(*+1)
2997        const PetscInt co = tmpO ? (rev ? -(tmpO[i]+1) : tmpO[i]) : 0; */
2998       PetscInt       co = tmpO ? tmpO[i] : 0;
2999       PetscInt       c;
3000 
3001       if (rev) {
3002         PetscInt childSize, coff;
3003         ierr = DMPlexGetConeSize(dm, cp, &childSize);CHKERRQ(ierr);
3004         coff = tmpO[i] < 0 ? -(tmpO[i]+1) : tmpO[i];
3005         co   = childSize ? -(((coff+childSize-1)%childSize)+1) : 0;
3006       }
3007       /* Check for duplicate */
3008       for (c = 0; c < closureSize; c += 2) {
3009         if (closure[c] == cp) break;
3010       }
3011       if (c == closureSize) {
3012         closure[closureSize]   = cp;
3013         closure[closureSize+1] = co;
3014         fifo[fifoSize]         = cp;
3015         fifo[fifoSize+1]       = co;
3016         closureSize           += 2;
3017         fifoSize              += 2;
3018       }
3019     }
3020     fifoStart += 2;
3021   }
3022   if (numPoints) *numPoints = closureSize/2;
3023   if (points)    *points    = closure;
3024   ierr = DMRestoreWorkArray(dm, maxSize, MPIU_INT, &fifo);CHKERRQ(ierr);
3025   PetscFunctionReturn(0);
3026 }
3027 
3028 /*@C
3029   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
3030 
3031   Not collective
3032 
3033   Input Parameters:
3034 + mesh - The DMPlex
3035 . p - The point, which must lie in the chart set with DMPlexSetChart()
3036 . useCone - PETSC_TRUE for in-edges,  otherwise use out-edges
3037 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints, zeroed on exit
3038 - points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...], zeroed on exit
3039 
3040   Note:
3041   If not using internal storage (points is not NULL on input), this call is unnecessary
3042 
3043   Fortran Notes:
3044   Since it returns an array, this routine is only available in Fortran 90, and you must
3045   include petsc.h90 in your code.
3046 
3047   The numPoints argument is not present in the Fortran 90 binding since it is internal to the array.
3048 
3049   Level: beginner
3050 
3051 .seealso: DMPlexGetTransitiveClosure(), DMPlexCreate(), DMPlexSetCone(), DMPlexSetChart(), DMPlexGetCone()
3052 @*/
3053 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3054 {
3055   PetscErrorCode ierr;
3056 
3057   PetscFunctionBegin;
3058   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3059   if (numPoints) PetscValidIntPointer(numPoints,4);
3060   if (points) PetscValidPointer(points,5);
3061   ierr = DMRestoreWorkArray(dm, 0, MPIU_INT, points);CHKERRQ(ierr);
3062   if (numPoints) *numPoints = 0;
3063   PetscFunctionReturn(0);
3064 }
3065 
3066 /*@
3067   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
3068 
3069   Not collective
3070 
3071   Input Parameter:
3072 . mesh - The DMPlex
3073 
3074   Output Parameters:
3075 + maxConeSize - The maximum number of in-edges
3076 - maxSupportSize - The maximum number of out-edges
3077 
3078   Level: beginner
3079 
3080 .seealso: DMPlexCreate(), DMPlexSetConeSize(), DMPlexSetChart()
3081 @*/
3082 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
3083 {
3084   DM_Plex *mesh = (DM_Plex*) dm->data;
3085 
3086   PetscFunctionBegin;
3087   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3088   if (maxConeSize)    *maxConeSize    = mesh->maxConeSize;
3089   if (maxSupportSize) *maxSupportSize = mesh->maxSupportSize;
3090   PetscFunctionReturn(0);
3091 }
3092 
3093 PetscErrorCode DMSetUp_Plex(DM dm)
3094 {
3095   DM_Plex       *mesh = (DM_Plex*) dm->data;
3096   PetscInt       size;
3097   PetscErrorCode ierr;
3098 
3099   PetscFunctionBegin;
3100   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3101   ierr = PetscSectionSetUp(mesh->coneSection);CHKERRQ(ierr);
3102   ierr = PetscSectionGetStorageSize(mesh->coneSection, &size);CHKERRQ(ierr);
3103   ierr = PetscMalloc1(size, &mesh->cones);CHKERRQ(ierr);
3104   ierr = PetscCalloc1(size, &mesh->coneOrientations);CHKERRQ(ierr);
3105   ierr = PetscLogObjectMemory((PetscObject) dm, size*2*sizeof(PetscInt));CHKERRQ(ierr);
3106   if (mesh->maxSupportSize) {
3107     ierr = PetscSectionSetUp(mesh->supportSection);CHKERRQ(ierr);
3108     ierr = PetscSectionGetStorageSize(mesh->supportSection, &size);CHKERRQ(ierr);
3109     ierr = PetscMalloc1(size, &mesh->supports);CHKERRQ(ierr);
3110     ierr = PetscLogObjectMemory((PetscObject) dm, size*sizeof(PetscInt));CHKERRQ(ierr);
3111   }
3112   PetscFunctionReturn(0);
3113 }
3114 
3115 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
3116 {
3117   PetscErrorCode ierr;
3118 
3119   PetscFunctionBegin;
3120   if (subdm) {ierr = DMClone(dm, subdm);CHKERRQ(ierr);}
3121   ierr = DMCreateSectionSubDM(dm, numFields, fields, is, subdm);CHKERRQ(ierr);
3122   if (subdm) {(*subdm)->useNatural = dm->useNatural;}
3123   if (dm->useNatural && dm->sfMigration) {
3124     PetscSF        sfMigrationInv,sfNatural;
3125     PetscSection   section, sectionSeq;
3126 
3127     (*subdm)->sfMigration = dm->sfMigration;
3128     ierr = PetscObjectReference((PetscObject) dm->sfMigration);CHKERRQ(ierr);
3129     ierr = DMGetLocalSection((*subdm), &section);CHKERRQ(ierr);
3130     ierr = PetscSFCreateInverseSF((*subdm)->sfMigration, &sfMigrationInv);CHKERRQ(ierr);
3131     ierr = PetscSectionCreate(PetscObjectComm((PetscObject) (*subdm)), &sectionSeq);CHKERRQ(ierr);
3132     ierr = PetscSFDistributeSection(sfMigrationInv, section, NULL, sectionSeq);CHKERRQ(ierr);
3133 
3134     ierr = DMPlexCreateGlobalToNaturalSF(*subdm, sectionSeq, (*subdm)->sfMigration, &sfNatural);CHKERRQ(ierr);
3135     (*subdm)->sfNatural = sfNatural;
3136     ierr = PetscSectionDestroy(&sectionSeq);CHKERRQ(ierr);
3137     ierr = PetscSFDestroy(&sfMigrationInv);CHKERRQ(ierr);
3138   }
3139   PetscFunctionReturn(0);
3140 }
3141 
3142 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
3143 {
3144   PetscErrorCode ierr;
3145   PetscInt       i = 0;
3146 
3147   PetscFunctionBegin;
3148   ierr = DMClone(dms[0], superdm);CHKERRQ(ierr);
3149   ierr = DMCreateSectionSuperDM(dms, len, is, superdm);CHKERRQ(ierr);
3150   (*superdm)->useNatural = PETSC_FALSE;
3151   for (i = 0; i < len; i++) {
3152     if (dms[i]->useNatural && dms[i]->sfMigration) {
3153       PetscSF        sfMigrationInv,sfNatural;
3154       PetscSection   section, sectionSeq;
3155 
3156       (*superdm)->sfMigration = dms[i]->sfMigration;
3157       ierr = PetscObjectReference((PetscObject) dms[i]->sfMigration);CHKERRQ(ierr);
3158       (*superdm)->useNatural = PETSC_TRUE;
3159       ierr = DMGetLocalSection((*superdm), &section);CHKERRQ(ierr);
3160       ierr = PetscSFCreateInverseSF((*superdm)->sfMigration, &sfMigrationInv);CHKERRQ(ierr);
3161       ierr = PetscSectionCreate(PetscObjectComm((PetscObject) (*superdm)), &sectionSeq);CHKERRQ(ierr);
3162       ierr = PetscSFDistributeSection(sfMigrationInv, section, NULL, sectionSeq);CHKERRQ(ierr);
3163 
3164       ierr = DMPlexCreateGlobalToNaturalSF(*superdm, sectionSeq, (*superdm)->sfMigration, &sfNatural);CHKERRQ(ierr);
3165       (*superdm)->sfNatural = sfNatural;
3166       ierr = PetscSectionDestroy(&sectionSeq);CHKERRQ(ierr);
3167       ierr = PetscSFDestroy(&sfMigrationInv);CHKERRQ(ierr);
3168       break;
3169     }
3170   }
3171   PetscFunctionReturn(0);
3172 }
3173 
3174 /*@
3175   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
3176 
3177   Not collective
3178 
3179   Input Parameter:
3180 . mesh - The DMPlex
3181 
3182   Output Parameter:
3183 
3184   Note:
3185   This should be called after all calls to DMPlexSetCone()
3186 
3187   Level: beginner
3188 
3189 .seealso: DMPlexCreate(), DMPlexSetChart(), DMPlexSetConeSize(), DMPlexSetCone()
3190 @*/
3191 PetscErrorCode DMPlexSymmetrize(DM dm)
3192 {
3193   DM_Plex       *mesh = (DM_Plex*) dm->data;
3194   PetscInt      *offsets;
3195   PetscInt       supportSize;
3196   PetscInt       pStart, pEnd, p;
3197   PetscErrorCode ierr;
3198 
3199   PetscFunctionBegin;
3200   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3201   if (mesh->supports) SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
3202   ierr = PetscLogEventBegin(DMPLEX_Symmetrize,dm,0,0,0);CHKERRQ(ierr);
3203   /* Calculate support sizes */
3204   ierr = DMPlexGetChart(dm, &pStart, &pEnd);CHKERRQ(ierr);
3205   for (p = pStart; p < pEnd; ++p) {
3206     PetscInt dof, off, c;
3207 
3208     ierr = PetscSectionGetDof(mesh->coneSection, p, &dof);CHKERRQ(ierr);
3209     ierr = PetscSectionGetOffset(mesh->coneSection, p, &off);CHKERRQ(ierr);
3210     for (c = off; c < off+dof; ++c) {
3211       ierr = PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1);CHKERRQ(ierr);
3212     }
3213   }
3214   for (p = pStart; p < pEnd; ++p) {
3215     PetscInt dof;
3216 
3217     ierr = PetscSectionGetDof(mesh->supportSection, p, &dof);CHKERRQ(ierr);
3218 
3219     mesh->maxSupportSize = PetscMax(mesh->maxSupportSize, dof);
3220   }
3221   ierr = PetscSectionSetUp(mesh->supportSection);CHKERRQ(ierr);
3222   /* Calculate supports */
3223   ierr = PetscSectionGetStorageSize(mesh->supportSection, &supportSize);CHKERRQ(ierr);
3224   ierr = PetscMalloc1(supportSize, &mesh->supports);CHKERRQ(ierr);
3225   ierr = PetscCalloc1(pEnd - pStart, &offsets);CHKERRQ(ierr);
3226   for (p = pStart; p < pEnd; ++p) {
3227     PetscInt dof, off, c;
3228 
3229     ierr = PetscSectionGetDof(mesh->coneSection, p, &dof);CHKERRQ(ierr);
3230     ierr = PetscSectionGetOffset(mesh->coneSection, p, &off);CHKERRQ(ierr);
3231     for (c = off; c < off+dof; ++c) {
3232       const PetscInt q = mesh->cones[c];
3233       PetscInt       offS;
3234 
3235       ierr = PetscSectionGetOffset(mesh->supportSection, q, &offS);CHKERRQ(ierr);
3236 
3237       mesh->supports[offS+offsets[q]] = p;
3238       ++offsets[q];
3239     }
3240   }
3241   ierr = PetscFree(offsets);CHKERRQ(ierr);
3242   ierr = PetscLogEventEnd(DMPLEX_Symmetrize,dm,0,0,0);CHKERRQ(ierr);
3243   PetscFunctionReturn(0);
3244 }
3245 
3246 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
3247 {
3248   IS             stratumIS;
3249   PetscErrorCode ierr;
3250 
3251   PetscFunctionBegin;
3252   if (pStart >= pEnd) PetscFunctionReturn(0);
3253   if (PetscDefined(USE_DEBUG)) {
3254     PetscInt  qStart, qEnd, numLevels, level;
3255     PetscBool overlap = PETSC_FALSE;
3256     ierr = DMLabelGetNumValues(label, &numLevels);CHKERRQ(ierr);
3257     for (level = 0; level < numLevels; level++) {
3258       ierr = DMLabelGetStratumBounds(label, level, &qStart, &qEnd);CHKERRQ(ierr);
3259       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {overlap = PETSC_TRUE; break;}
3260     }
3261     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);
3262   }
3263   ierr = ISCreateStride(PETSC_COMM_SELF, pEnd-pStart, pStart, 1, &stratumIS);CHKERRQ(ierr);
3264   ierr = DMLabelSetStratumIS(label, depth, stratumIS);CHKERRQ(ierr);
3265   ierr = ISDestroy(&stratumIS);CHKERRQ(ierr);
3266   PetscFunctionReturn(0);
3267 }
3268 
3269 /*@
3270   DMPlexStratify - The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
3271   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram). The strata group all points of the
3272   same grade, and this function calculates the strata. This grade can be seen as the height (or depth) of the point in
3273   the DAG.
3274 
3275   Collective on dm
3276 
3277   Input Parameter:
3278 . mesh - The DMPlex
3279 
3280   Output Parameter:
3281 
3282   Notes:
3283   Concretely, DMPlexStratify() creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
3284   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
3285   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through DMPlexGetDepthLabel() or DMPlexGetDepthStratum(), or
3286   manually via DMGetLabel().  The height is defined implicitly by height = maxDimension - depth, and can be accessed
3287   via DMPlexGetHeightStratum().  For example, cells have height 0 and faces have height 1.
3288 
3289   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
3290   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
3291   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
3292   to interpolate only that one (e0), so that
3293 $  cone(c0) = {e0, v2}
3294 $  cone(e0) = {v0, v1}
3295   If DMPlexStratify() is run on this mesh, it will give depths
3296 $  depth 0 = {v0, v1, v2}
3297 $  depth 1 = {e0, c0}
3298   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
3299 
3300   DMPlexStratify() should be called after all calls to DMPlexSymmetrize()
3301 
3302   Level: beginner
3303 
3304 .seealso: DMPlexCreate(), DMPlexSymmetrize(), DMPlexComputeCellTypes()
3305 @*/
3306 PetscErrorCode DMPlexStratify(DM dm)
3307 {
3308   DM_Plex       *mesh = (DM_Plex*) dm->data;
3309   DMLabel        label;
3310   PetscInt       pStart, pEnd, p;
3311   PetscInt       numRoots = 0, numLeaves = 0;
3312   PetscErrorCode ierr;
3313 
3314   PetscFunctionBegin;
3315   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3316   ierr = PetscLogEventBegin(DMPLEX_Stratify,dm,0,0,0);CHKERRQ(ierr);
3317 
3318   /* Create depth label */
3319   ierr = DMPlexGetChart(dm, &pStart, &pEnd);CHKERRQ(ierr);
3320   ierr = DMCreateLabel(dm, "depth");CHKERRQ(ierr);
3321   ierr = DMPlexGetDepthLabel(dm, &label);CHKERRQ(ierr);
3322 
3323   {
3324     /* Initialize roots and count leaves */
3325     PetscInt sMin = PETSC_MAX_INT;
3326     PetscInt sMax = PETSC_MIN_INT;
3327     PetscInt coneSize, supportSize;
3328 
3329     for (p = pStart; p < pEnd; ++p) {
3330       ierr = DMPlexGetConeSize(dm, p, &coneSize);CHKERRQ(ierr);
3331       ierr = DMPlexGetSupportSize(dm, p, &supportSize);CHKERRQ(ierr);
3332       if (!coneSize && supportSize) {
3333         sMin = PetscMin(p, sMin);
3334         sMax = PetscMax(p, sMax);
3335         ++numRoots;
3336       } else if (!supportSize && coneSize) {
3337         ++numLeaves;
3338       } else if (!supportSize && !coneSize) {
3339         /* Isolated points */
3340         sMin = PetscMin(p, sMin);
3341         sMax = PetscMax(p, sMax);
3342       }
3343     }
3344     ierr = DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax+1);CHKERRQ(ierr);
3345   }
3346 
3347   if (numRoots + numLeaves == (pEnd - pStart)) {
3348     PetscInt sMin = PETSC_MAX_INT;
3349     PetscInt sMax = PETSC_MIN_INT;
3350     PetscInt coneSize, supportSize;
3351 
3352     for (p = pStart; p < pEnd; ++p) {
3353       ierr = DMPlexGetConeSize(dm, p, &coneSize);CHKERRQ(ierr);
3354       ierr = DMPlexGetSupportSize(dm, p, &supportSize);CHKERRQ(ierr);
3355       if (!supportSize && coneSize) {
3356         sMin = PetscMin(p, sMin);
3357         sMax = PetscMax(p, sMax);
3358       }
3359     }
3360     ierr = DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax+1);CHKERRQ(ierr);
3361   } else {
3362     PetscInt level = 0;
3363     PetscInt qStart, qEnd, q;
3364 
3365     ierr = DMLabelGetStratumBounds(label, level, &qStart, &qEnd);CHKERRQ(ierr);
3366     while (qEnd > qStart) {
3367       PetscInt sMin = PETSC_MAX_INT;
3368       PetscInt sMax = PETSC_MIN_INT;
3369 
3370       for (q = qStart; q < qEnd; ++q) {
3371         const PetscInt *support;
3372         PetscInt        supportSize, s;
3373 
3374         ierr = DMPlexGetSupportSize(dm, q, &supportSize);CHKERRQ(ierr);
3375         ierr = DMPlexGetSupport(dm, q, &support);CHKERRQ(ierr);
3376         for (s = 0; s < supportSize; ++s) {
3377           sMin = PetscMin(support[s], sMin);
3378           sMax = PetscMax(support[s], sMax);
3379         }
3380       }
3381       ierr = DMLabelGetNumValues(label, &level);CHKERRQ(ierr);
3382       ierr = DMPlexCreateDepthStratum(dm, label, level, sMin, sMax+1);CHKERRQ(ierr);
3383       ierr = DMLabelGetStratumBounds(label, level, &qStart, &qEnd);CHKERRQ(ierr);
3384     }
3385   }
3386   { /* just in case there is an empty process */
3387     PetscInt numValues, maxValues = 0, v;
3388 
3389     ierr = DMLabelGetNumValues(label, &numValues);CHKERRQ(ierr);
3390     ierr = MPI_Allreduce(&numValues,&maxValues,1,MPIU_INT,MPI_MAX,PetscObjectComm((PetscObject)dm));CHKERRMPI(ierr);
3391     for (v = numValues; v < maxValues; v++) {
3392       ierr = DMLabelAddStratum(label, v);CHKERRQ(ierr);
3393     }
3394   }
3395   ierr = PetscObjectStateGet((PetscObject) label, &mesh->depthState);CHKERRQ(ierr);
3396   ierr = PetscLogEventEnd(DMPLEX_Stratify,dm,0,0,0);CHKERRQ(ierr);
3397   PetscFunctionReturn(0);
3398 }
3399 
3400 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
3401 {
3402   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
3403   PetscInt       dim, depth, pheight, coneSize;
3404   PetscErrorCode ierr;
3405 
3406   PetscFunctionBeginHot;
3407   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
3408   ierr = DMPlexGetDepth(dm, &depth);CHKERRQ(ierr);
3409   ierr = DMPlexGetConeSize(dm, p, &coneSize);CHKERRQ(ierr);
3410   pheight = depth - pdepth;
3411   if (depth <= 1) {
3412     switch (pdepth) {
3413       case 0: ct = DM_POLYTOPE_POINT;break;
3414       case 1:
3415         switch (coneSize) {
3416           case 2: ct = DM_POLYTOPE_SEGMENT;break;
3417           case 3: ct = DM_POLYTOPE_TRIANGLE;break;
3418           case 4:
3419           switch (dim) {
3420             case 2: ct = DM_POLYTOPE_QUADRILATERAL;break;
3421             case 3: ct = DM_POLYTOPE_TETRAHEDRON;break;
3422             default: break;
3423           }
3424           break;
3425         case 5: ct = DM_POLYTOPE_PYRAMID;break;
3426         case 6: ct = DM_POLYTOPE_TRI_PRISM_TENSOR;break;
3427         case 8: ct = DM_POLYTOPE_HEXAHEDRON;break;
3428         default: break;
3429       }
3430     }
3431   } else {
3432     if (pdepth == 0) {
3433       ct = DM_POLYTOPE_POINT;
3434     } else if (pheight == 0) {
3435       switch (dim) {
3436         case 1:
3437           switch (coneSize) {
3438             case 2: ct = DM_POLYTOPE_SEGMENT;break;
3439             default: break;
3440           }
3441           break;
3442         case 2:
3443           switch (coneSize) {
3444             case 3: ct = DM_POLYTOPE_TRIANGLE;break;
3445             case 4: ct = DM_POLYTOPE_QUADRILATERAL;break;
3446             default: break;
3447           }
3448           break;
3449         case 3:
3450           switch (coneSize) {
3451             case 4: ct = DM_POLYTOPE_TETRAHEDRON;break;
3452             case 5:
3453             {
3454               const PetscInt *cone;
3455               PetscInt        faceConeSize;
3456 
3457               ierr = DMPlexGetCone(dm, p, &cone);CHKERRQ(ierr);
3458               ierr = DMPlexGetConeSize(dm, cone[0], &faceConeSize);CHKERRQ(ierr);
3459               switch (faceConeSize) {
3460                 case 3: ct = DM_POLYTOPE_TRI_PRISM_TENSOR;break;
3461                 case 4: ct = DM_POLYTOPE_PYRAMID;break;
3462               }
3463             }
3464             break;
3465             case 6: ct = DM_POLYTOPE_HEXAHEDRON;break;
3466             default: break;
3467           }
3468           break;
3469         default: break;
3470       }
3471     } else if (pheight > 0) {
3472       switch (coneSize) {
3473         case 2: ct = DM_POLYTOPE_SEGMENT;break;
3474         case 3: ct = DM_POLYTOPE_TRIANGLE;break;
3475         case 4: ct = DM_POLYTOPE_QUADRILATERAL;break;
3476         default: break;
3477       }
3478     }
3479   }
3480   *pt = ct;
3481   PetscFunctionReturn(0);
3482 }
3483 
3484 /*@
3485   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
3486 
3487   Collective on dm
3488 
3489   Input Parameter:
3490 . mesh - The DMPlex
3491 
3492   DMPlexComputeCellTypes() should be called after all calls to DMPlexSymmetrize() and DMPlexStratify()
3493 
3494   Level: developer
3495 
3496   Note: This function is normally called automatically by Plex when a cell type is requested. It creates an
3497   internal DMLabel named "celltype" which can be directly accessed using DMGetLabel(). A user may disable
3498   automatic creation by creating the label manually, using DMCreateLabel(dm, "celltype").
3499 
3500 .seealso: DMPlexCreate(), DMPlexSymmetrize(), DMPlexStratify(), DMGetLabel(), DMCreateLabel()
3501 @*/
3502 PetscErrorCode DMPlexComputeCellTypes(DM dm)
3503 {
3504   DM_Plex       *mesh;
3505   DMLabel        ctLabel;
3506   PetscInt       pStart, pEnd, p;
3507   PetscErrorCode ierr;
3508 
3509   PetscFunctionBegin;
3510   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3511   mesh = (DM_Plex *) dm->data;
3512   ierr = DMCreateLabel(dm, "celltype");CHKERRQ(ierr);
3513   ierr = DMPlexGetCellTypeLabel(dm, &ctLabel);CHKERRQ(ierr);
3514   ierr = DMPlexGetChart(dm, &pStart, &pEnd);CHKERRQ(ierr);
3515   for (p = pStart; p < pEnd; ++p) {
3516     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
3517     PetscInt       pdepth;
3518 
3519     ierr = DMPlexGetPointDepth(dm, p, &pdepth);CHKERRQ(ierr);
3520     ierr = DMPlexComputeCellType_Internal(dm, p, pdepth, &ct);CHKERRQ(ierr);
3521     if (ct == DM_POLYTOPE_UNKNOWN) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %D is screwed up", p);
3522     ierr = DMLabelSetValue(ctLabel, p, ct);CHKERRQ(ierr);
3523   }
3524   ierr = PetscObjectStateGet((PetscObject) ctLabel, &mesh->celltypeState);CHKERRQ(ierr);
3525   ierr = PetscObjectViewFromOptions((PetscObject) ctLabel, NULL, "-dm_plex_celltypes_view");CHKERRQ(ierr);
3526   PetscFunctionReturn(0);
3527 }
3528 
3529 /*@C
3530   DMPlexGetJoin - Get an array for the join of the set of points
3531 
3532   Not Collective
3533 
3534   Input Parameters:
3535 + dm - The DMPlex object
3536 . numPoints - The number of input points for the join
3537 - points - The input points
3538 
3539   Output Parameters:
3540 + numCoveredPoints - The number of points in the join
3541 - coveredPoints - The points in the join
3542 
3543   Level: intermediate
3544 
3545   Note: Currently, this is restricted to a single level join
3546 
3547   Fortran Notes:
3548   Since it returns an array, this routine is only available in Fortran 90, and you must
3549   include petsc.h90 in your code.
3550 
3551   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
3552 
3553 .seealso: DMPlexRestoreJoin(), DMPlexGetMeet()
3554 @*/
3555 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
3556 {
3557   DM_Plex       *mesh = (DM_Plex*) dm->data;
3558   PetscInt      *join[2];
3559   PetscInt       joinSize, i = 0;
3560   PetscInt       dof, off, p, c, m;
3561   PetscErrorCode ierr;
3562 
3563   PetscFunctionBegin;
3564   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3565   PetscValidIntPointer(points, 3);
3566   PetscValidIntPointer(numCoveredPoints, 4);
3567   PetscValidPointer(coveredPoints, 5);
3568   ierr = DMGetWorkArray(dm, mesh->maxSupportSize, MPIU_INT, &join[0]);CHKERRQ(ierr);
3569   ierr = DMGetWorkArray(dm, mesh->maxSupportSize, MPIU_INT, &join[1]);CHKERRQ(ierr);
3570   /* Copy in support of first point */
3571   ierr = PetscSectionGetDof(mesh->supportSection, points[0], &dof);CHKERRQ(ierr);
3572   ierr = PetscSectionGetOffset(mesh->supportSection, points[0], &off);CHKERRQ(ierr);
3573   for (joinSize = 0; joinSize < dof; ++joinSize) {
3574     join[i][joinSize] = mesh->supports[off+joinSize];
3575   }
3576   /* Check each successive support */
3577   for (p = 1; p < numPoints; ++p) {
3578     PetscInt newJoinSize = 0;
3579 
3580     ierr = PetscSectionGetDof(mesh->supportSection, points[p], &dof);CHKERRQ(ierr);
3581     ierr = PetscSectionGetOffset(mesh->supportSection, points[p], &off);CHKERRQ(ierr);
3582     for (c = 0; c < dof; ++c) {
3583       const PetscInt point = mesh->supports[off+c];
3584 
3585       for (m = 0; m < joinSize; ++m) {
3586         if (point == join[i][m]) {
3587           join[1-i][newJoinSize++] = point;
3588           break;
3589         }
3590       }
3591     }
3592     joinSize = newJoinSize;
3593     i        = 1-i;
3594   }
3595   *numCoveredPoints = joinSize;
3596   *coveredPoints    = join[i];
3597   ierr              = DMRestoreWorkArray(dm, mesh->maxSupportSize, MPIU_INT, &join[1-i]);CHKERRQ(ierr);
3598   PetscFunctionReturn(0);
3599 }
3600 
3601 /*@C
3602   DMPlexRestoreJoin - Restore an array for the join of the set of points
3603 
3604   Not Collective
3605 
3606   Input Parameters:
3607 + dm - The DMPlex object
3608 . numPoints - The number of input points for the join
3609 - points - The input points
3610 
3611   Output Parameters:
3612 + numCoveredPoints - The number of points in the join
3613 - coveredPoints - The points in the join
3614 
3615   Fortran Notes:
3616   Since it returns an array, this routine is only available in Fortran 90, and you must
3617   include petsc.h90 in your code.
3618 
3619   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
3620 
3621   Level: intermediate
3622 
3623 .seealso: DMPlexGetJoin(), DMPlexGetFullJoin(), DMPlexGetMeet()
3624 @*/
3625 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
3626 {
3627   PetscErrorCode ierr;
3628 
3629   PetscFunctionBegin;
3630   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3631   if (points) PetscValidIntPointer(points,3);
3632   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints,4);
3633   PetscValidPointer(coveredPoints, 5);
3634   ierr = DMRestoreWorkArray(dm, 0, MPIU_INT, (void*) coveredPoints);CHKERRQ(ierr);
3635   if (numCoveredPoints) *numCoveredPoints = 0;
3636   PetscFunctionReturn(0);
3637 }
3638 
3639 /*@C
3640   DMPlexGetFullJoin - Get an array for the join of the set of points
3641 
3642   Not Collective
3643 
3644   Input Parameters:
3645 + dm - The DMPlex object
3646 . numPoints - The number of input points for the join
3647 - points - The input points
3648 
3649   Output Parameters:
3650 + numCoveredPoints - The number of points in the join
3651 - coveredPoints - The points in the join
3652 
3653   Fortran Notes:
3654   Since it returns an array, this routine is only available in Fortran 90, and you must
3655   include petsc.h90 in your code.
3656 
3657   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
3658 
3659   Level: intermediate
3660 
3661 .seealso: DMPlexGetJoin(), DMPlexRestoreJoin(), DMPlexGetMeet()
3662 @*/
3663 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
3664 {
3665   DM_Plex       *mesh = (DM_Plex*) dm->data;
3666   PetscInt      *offsets, **closures;
3667   PetscInt      *join[2];
3668   PetscInt       depth = 0, maxSize, joinSize = 0, i = 0;
3669   PetscInt       p, d, c, m, ms;
3670   PetscErrorCode ierr;
3671 
3672   PetscFunctionBegin;
3673   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3674   PetscValidIntPointer(points, 3);
3675   PetscValidIntPointer(numCoveredPoints, 4);
3676   PetscValidPointer(coveredPoints, 5);
3677 
3678   ierr    = DMPlexGetDepth(dm, &depth);CHKERRQ(ierr);
3679   ierr    = PetscCalloc1(numPoints, &closures);CHKERRQ(ierr);
3680   ierr    = DMGetWorkArray(dm, numPoints*(depth+2), MPIU_INT, &offsets);CHKERRQ(ierr);
3681   ms      = mesh->maxSupportSize;
3682   maxSize = (ms > 1) ? ((PetscPowInt(ms,depth+1)-1)/(ms-1)) : depth + 1;
3683   ierr    = DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]);CHKERRQ(ierr);
3684   ierr    = DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]);CHKERRQ(ierr);
3685 
3686   for (p = 0; p < numPoints; ++p) {
3687     PetscInt closureSize;
3688 
3689     ierr = DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]);CHKERRQ(ierr);
3690 
3691     offsets[p*(depth+2)+0] = 0;
3692     for (d = 0; d < depth+1; ++d) {
3693       PetscInt pStart, pEnd, i;
3694 
3695       ierr = DMPlexGetDepthStratum(dm, d, &pStart, &pEnd);CHKERRQ(ierr);
3696       for (i = offsets[p*(depth+2)+d]; i < closureSize; ++i) {
3697         if ((pStart > closures[p][i*2]) || (pEnd <= closures[p][i*2])) {
3698           offsets[p*(depth+2)+d+1] = i;
3699           break;
3700         }
3701       }
3702       if (i == closureSize) offsets[p*(depth+2)+d+1] = i;
3703     }
3704     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);
3705   }
3706   for (d = 0; d < depth+1; ++d) {
3707     PetscInt dof;
3708 
3709     /* Copy in support of first point */
3710     dof = offsets[d+1] - offsets[d];
3711     for (joinSize = 0; joinSize < dof; ++joinSize) {
3712       join[i][joinSize] = closures[0][(offsets[d]+joinSize)*2];
3713     }
3714     /* Check each successive cone */
3715     for (p = 1; p < numPoints && joinSize; ++p) {
3716       PetscInt newJoinSize = 0;
3717 
3718       dof = offsets[p*(depth+2)+d+1] - offsets[p*(depth+2)+d];
3719       for (c = 0; c < dof; ++c) {
3720         const PetscInt point = closures[p][(offsets[p*(depth+2)+d]+c)*2];
3721 
3722         for (m = 0; m < joinSize; ++m) {
3723           if (point == join[i][m]) {
3724             join[1-i][newJoinSize++] = point;
3725             break;
3726           }
3727         }
3728       }
3729       joinSize = newJoinSize;
3730       i        = 1-i;
3731     }
3732     if (joinSize) break;
3733   }
3734   *numCoveredPoints = joinSize;
3735   *coveredPoints    = join[i];
3736   for (p = 0; p < numPoints; ++p) {
3737     ierr = DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]);CHKERRQ(ierr);
3738   }
3739   ierr = PetscFree(closures);CHKERRQ(ierr);
3740   ierr = DMRestoreWorkArray(dm, numPoints*(depth+2), MPIU_INT, &offsets);CHKERRQ(ierr);
3741   ierr = DMRestoreWorkArray(dm, mesh->maxSupportSize, MPIU_INT, &join[1-i]);CHKERRQ(ierr);
3742   PetscFunctionReturn(0);
3743 }
3744 
3745 /*@C
3746   DMPlexGetMeet - Get an array for the meet of the set of points
3747 
3748   Not Collective
3749 
3750   Input Parameters:
3751 + dm - The DMPlex object
3752 . numPoints - The number of input points for the meet
3753 - points - The input points
3754 
3755   Output Parameters:
3756 + numCoveredPoints - The number of points in the meet
3757 - coveredPoints - The points in the meet
3758 
3759   Level: intermediate
3760 
3761   Note: Currently, this is restricted to a single level meet
3762 
3763   Fortran Notes:
3764   Since it returns an array, this routine is only available in Fortran 90, and you must
3765   include petsc.h90 in your code.
3766 
3767   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
3768 
3769 .seealso: DMPlexRestoreMeet(), DMPlexGetJoin()
3770 @*/
3771 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
3772 {
3773   DM_Plex       *mesh = (DM_Plex*) dm->data;
3774   PetscInt      *meet[2];
3775   PetscInt       meetSize, i = 0;
3776   PetscInt       dof, off, p, c, m;
3777   PetscErrorCode ierr;
3778 
3779   PetscFunctionBegin;
3780   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3781   PetscValidPointer(points, 3);
3782   PetscValidPointer(numCoveringPoints, 4);
3783   PetscValidPointer(coveringPoints, 5);
3784   ierr = DMGetWorkArray(dm, mesh->maxConeSize, MPIU_INT, &meet[0]);CHKERRQ(ierr);
3785   ierr = DMGetWorkArray(dm, mesh->maxConeSize, MPIU_INT, &meet[1]);CHKERRQ(ierr);
3786   /* Copy in cone of first point */
3787   ierr = PetscSectionGetDof(mesh->coneSection, points[0], &dof);CHKERRQ(ierr);
3788   ierr = PetscSectionGetOffset(mesh->coneSection, points[0], &off);CHKERRQ(ierr);
3789   for (meetSize = 0; meetSize < dof; ++meetSize) {
3790     meet[i][meetSize] = mesh->cones[off+meetSize];
3791   }
3792   /* Check each successive cone */
3793   for (p = 1; p < numPoints; ++p) {
3794     PetscInt newMeetSize = 0;
3795 
3796     ierr = PetscSectionGetDof(mesh->coneSection, points[p], &dof);CHKERRQ(ierr);
3797     ierr = PetscSectionGetOffset(mesh->coneSection, points[p], &off);CHKERRQ(ierr);
3798     for (c = 0; c < dof; ++c) {
3799       const PetscInt point = mesh->cones[off+c];
3800 
3801       for (m = 0; m < meetSize; ++m) {
3802         if (point == meet[i][m]) {
3803           meet[1-i][newMeetSize++] = point;
3804           break;
3805         }
3806       }
3807     }
3808     meetSize = newMeetSize;
3809     i        = 1-i;
3810   }
3811   *numCoveringPoints = meetSize;
3812   *coveringPoints    = meet[i];
3813   ierr               = DMRestoreWorkArray(dm, mesh->maxConeSize, MPIU_INT, &meet[1-i]);CHKERRQ(ierr);
3814   PetscFunctionReturn(0);
3815 }
3816 
3817 /*@C
3818   DMPlexRestoreMeet - Restore an array for the meet of the set of points
3819 
3820   Not Collective
3821 
3822   Input Parameters:
3823 + dm - The DMPlex object
3824 . numPoints - The number of input points for the meet
3825 - points - The input points
3826 
3827   Output Parameters:
3828 + numCoveredPoints - The number of points in the meet
3829 - coveredPoints - The points in the meet
3830 
3831   Level: intermediate
3832 
3833   Fortran Notes:
3834   Since it returns an array, this routine is only available in Fortran 90, and you must
3835   include petsc.h90 in your code.
3836 
3837   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
3838 
3839 .seealso: DMPlexGetMeet(), DMPlexGetFullMeet(), DMPlexGetJoin()
3840 @*/
3841 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
3842 {
3843   PetscErrorCode ierr;
3844 
3845   PetscFunctionBegin;
3846   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3847   if (points) PetscValidIntPointer(points,3);
3848   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints,4);
3849   PetscValidPointer(coveredPoints,5);
3850   ierr = DMRestoreWorkArray(dm, 0, MPIU_INT, (void*) coveredPoints);CHKERRQ(ierr);
3851   if (numCoveredPoints) *numCoveredPoints = 0;
3852   PetscFunctionReturn(0);
3853 }
3854 
3855 /*@C
3856   DMPlexGetFullMeet - Get an array for the meet of the set of points
3857 
3858   Not Collective
3859 
3860   Input Parameters:
3861 + dm - The DMPlex object
3862 . numPoints - The number of input points for the meet
3863 - points - The input points
3864 
3865   Output Parameters:
3866 + numCoveredPoints - The number of points in the meet
3867 - coveredPoints - The points in the meet
3868 
3869   Level: intermediate
3870 
3871   Fortran Notes:
3872   Since it returns an array, this routine is only available in Fortran 90, and you must
3873   include petsc.h90 in your code.
3874 
3875   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
3876 
3877 .seealso: DMPlexGetMeet(), DMPlexRestoreMeet(), DMPlexGetJoin()
3878 @*/
3879 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
3880 {
3881   DM_Plex       *mesh = (DM_Plex*) dm->data;
3882   PetscInt      *offsets, **closures;
3883   PetscInt      *meet[2];
3884   PetscInt       height = 0, maxSize, meetSize = 0, i = 0;
3885   PetscInt       p, h, c, m, mc;
3886   PetscErrorCode ierr;
3887 
3888   PetscFunctionBegin;
3889   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3890   PetscValidPointer(points, 3);
3891   PetscValidPointer(numCoveredPoints, 4);
3892   PetscValidPointer(coveredPoints, 5);
3893 
3894   ierr    = DMPlexGetDepth(dm, &height);CHKERRQ(ierr);
3895   ierr    = PetscMalloc1(numPoints, &closures);CHKERRQ(ierr);
3896   ierr    = DMGetWorkArray(dm, numPoints*(height+2), MPIU_INT, &offsets);CHKERRQ(ierr);
3897   mc      = mesh->maxConeSize;
3898   maxSize = (mc > 1) ? ((PetscPowInt(mc,height+1)-1)/(mc-1)) : height + 1;
3899   ierr    = DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]);CHKERRQ(ierr);
3900   ierr    = DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]);CHKERRQ(ierr);
3901 
3902   for (p = 0; p < numPoints; ++p) {
3903     PetscInt closureSize;
3904 
3905     ierr = DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]);CHKERRQ(ierr);
3906 
3907     offsets[p*(height+2)+0] = 0;
3908     for (h = 0; h < height+1; ++h) {
3909       PetscInt pStart, pEnd, i;
3910 
3911       ierr = DMPlexGetHeightStratum(dm, h, &pStart, &pEnd);CHKERRQ(ierr);
3912       for (i = offsets[p*(height+2)+h]; i < closureSize; ++i) {
3913         if ((pStart > closures[p][i*2]) || (pEnd <= closures[p][i*2])) {
3914           offsets[p*(height+2)+h+1] = i;
3915           break;
3916         }
3917       }
3918       if (i == closureSize) offsets[p*(height+2)+h+1] = i;
3919     }
3920     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);
3921   }
3922   for (h = 0; h < height+1; ++h) {
3923     PetscInt dof;
3924 
3925     /* Copy in cone of first point */
3926     dof = offsets[h+1] - offsets[h];
3927     for (meetSize = 0; meetSize < dof; ++meetSize) {
3928       meet[i][meetSize] = closures[0][(offsets[h]+meetSize)*2];
3929     }
3930     /* Check each successive cone */
3931     for (p = 1; p < numPoints && meetSize; ++p) {
3932       PetscInt newMeetSize = 0;
3933 
3934       dof = offsets[p*(height+2)+h+1] - offsets[p*(height+2)+h];
3935       for (c = 0; c < dof; ++c) {
3936         const PetscInt point = closures[p][(offsets[p*(height+2)+h]+c)*2];
3937 
3938         for (m = 0; m < meetSize; ++m) {
3939           if (point == meet[i][m]) {
3940             meet[1-i][newMeetSize++] = point;
3941             break;
3942           }
3943         }
3944       }
3945       meetSize = newMeetSize;
3946       i        = 1-i;
3947     }
3948     if (meetSize) break;
3949   }
3950   *numCoveredPoints = meetSize;
3951   *coveredPoints    = meet[i];
3952   for (p = 0; p < numPoints; ++p) {
3953     ierr = DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]);CHKERRQ(ierr);
3954   }
3955   ierr = PetscFree(closures);CHKERRQ(ierr);
3956   ierr = DMRestoreWorkArray(dm, numPoints*(height+2), MPIU_INT, &offsets);CHKERRQ(ierr);
3957   ierr = DMRestoreWorkArray(dm, mesh->maxConeSize, MPIU_INT, &meet[1-i]);CHKERRQ(ierr);
3958   PetscFunctionReturn(0);
3959 }
3960 
3961 /*@C
3962   DMPlexEqual - Determine if two DMs have the same topology
3963 
3964   Not Collective
3965 
3966   Input Parameters:
3967 + dmA - A DMPlex object
3968 - dmB - A DMPlex object
3969 
3970   Output Parameters:
3971 . equal - PETSC_TRUE if the topologies are identical
3972 
3973   Level: intermediate
3974 
3975   Notes:
3976   We are not solving graph isomorphism, so we do not permutation.
3977 
3978 .seealso: DMPlexGetCone()
3979 @*/
3980 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
3981 {
3982   PetscInt       depth, depthB, pStart, pEnd, pStartB, pEndB, p;
3983   PetscErrorCode ierr;
3984 
3985   PetscFunctionBegin;
3986   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
3987   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
3988   PetscValidPointer(equal, 3);
3989 
3990   *equal = PETSC_FALSE;
3991   ierr = DMPlexGetDepth(dmA, &depth);CHKERRQ(ierr);
3992   ierr = DMPlexGetDepth(dmB, &depthB);CHKERRQ(ierr);
3993   if (depth != depthB) PetscFunctionReturn(0);
3994   ierr = DMPlexGetChart(dmA, &pStart,  &pEnd);CHKERRQ(ierr);
3995   ierr = DMPlexGetChart(dmB, &pStartB, &pEndB);CHKERRQ(ierr);
3996   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(0);
3997   for (p = pStart; p < pEnd; ++p) {
3998     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
3999     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
4000 
4001     ierr = DMPlexGetConeSize(dmA, p, &coneSize);CHKERRQ(ierr);
4002     ierr = DMPlexGetCone(dmA, p, &cone);CHKERRQ(ierr);
4003     ierr = DMPlexGetConeOrientation(dmA, p, &ornt);CHKERRQ(ierr);
4004     ierr = DMPlexGetConeSize(dmB, p, &coneSizeB);CHKERRQ(ierr);
4005     ierr = DMPlexGetCone(dmB, p, &coneB);CHKERRQ(ierr);
4006     ierr = DMPlexGetConeOrientation(dmB, p, &orntB);CHKERRQ(ierr);
4007     if (coneSize != coneSizeB) PetscFunctionReturn(0);
4008     for (c = 0; c < coneSize; ++c) {
4009       if (cone[c] != coneB[c]) PetscFunctionReturn(0);
4010       if (ornt[c] != orntB[c]) PetscFunctionReturn(0);
4011     }
4012     ierr = DMPlexGetSupportSize(dmA, p, &supportSize);CHKERRQ(ierr);
4013     ierr = DMPlexGetSupport(dmA, p, &support);CHKERRQ(ierr);
4014     ierr = DMPlexGetSupportSize(dmB, p, &supportSizeB);CHKERRQ(ierr);
4015     ierr = DMPlexGetSupport(dmB, p, &supportB);CHKERRQ(ierr);
4016     if (supportSize != supportSizeB) PetscFunctionReturn(0);
4017     for (s = 0; s < supportSize; ++s) {
4018       if (support[s] != supportB[s]) PetscFunctionReturn(0);
4019     }
4020   }
4021   *equal = PETSC_TRUE;
4022   PetscFunctionReturn(0);
4023 }
4024 
4025 /*@C
4026   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
4027 
4028   Not Collective
4029 
4030   Input Parameters:
4031 + dm         - The DMPlex
4032 . cellDim    - The cell dimension
4033 - numCorners - The number of vertices on a cell
4034 
4035   Output Parameters:
4036 . numFaceVertices - The number of vertices on a face
4037 
4038   Level: developer
4039 
4040   Notes:
4041   Of course this can only work for a restricted set of symmetric shapes
4042 
4043 .seealso: DMPlexGetCone()
4044 @*/
4045 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
4046 {
4047   MPI_Comm       comm;
4048   PetscErrorCode ierr;
4049 
4050   PetscFunctionBegin;
4051   ierr = PetscObjectGetComm((PetscObject)dm,&comm);CHKERRQ(ierr);
4052   PetscValidPointer(numFaceVertices,4);
4053   switch (cellDim) {
4054   case 0:
4055     *numFaceVertices = 0;
4056     break;
4057   case 1:
4058     *numFaceVertices = 1;
4059     break;
4060   case 2:
4061     switch (numCorners) {
4062     case 3: /* triangle */
4063       *numFaceVertices = 2; /* Edge has 2 vertices */
4064       break;
4065     case 4: /* quadrilateral */
4066       *numFaceVertices = 2; /* Edge has 2 vertices */
4067       break;
4068     case 6: /* quadratic triangle, tri and quad cohesive Lagrange cells */
4069       *numFaceVertices = 3; /* Edge has 3 vertices */
4070       break;
4071     case 9: /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
4072       *numFaceVertices = 3; /* Edge has 3 vertices */
4073       break;
4074     default:
4075       SETERRQ2(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %D for dimension %D", numCorners, cellDim);
4076     }
4077     break;
4078   case 3:
4079     switch (numCorners) {
4080     case 4: /* tetradehdron */
4081       *numFaceVertices = 3; /* Face has 3 vertices */
4082       break;
4083     case 6: /* tet cohesive cells */
4084       *numFaceVertices = 4; /* Face has 4 vertices */
4085       break;
4086     case 8: /* hexahedron */
4087       *numFaceVertices = 4; /* Face has 4 vertices */
4088       break;
4089     case 9: /* tet cohesive Lagrange cells */
4090       *numFaceVertices = 6; /* Face has 6 vertices */
4091       break;
4092     case 10: /* quadratic tetrahedron */
4093       *numFaceVertices = 6; /* Face has 6 vertices */
4094       break;
4095     case 12: /* hex cohesive Lagrange cells */
4096       *numFaceVertices = 6; /* Face has 6 vertices */
4097       break;
4098     case 18: /* quadratic tet cohesive Lagrange cells */
4099       *numFaceVertices = 6; /* Face has 6 vertices */
4100       break;
4101     case 27: /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
4102       *numFaceVertices = 9; /* Face has 9 vertices */
4103       break;
4104     default:
4105       SETERRQ2(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %D for dimension %D", numCorners, cellDim);
4106     }
4107     break;
4108   default:
4109     SETERRQ1(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %D", cellDim);
4110   }
4111   PetscFunctionReturn(0);
4112 }
4113 
4114 /*@
4115   DMPlexGetDepthLabel - Get the DMLabel recording the depth of each point
4116 
4117   Not Collective
4118 
4119   Input Parameter:
4120 . dm    - The DMPlex object
4121 
4122   Output Parameter:
4123 . depthLabel - The DMLabel recording point depth
4124 
4125   Level: developer
4126 
4127 .seealso: DMPlexGetDepth(), DMPlexGetHeightStratum(), DMPlexGetDepthStratum(), DMPlexGetPointDepth(),
4128 @*/
4129 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
4130 {
4131   PetscFunctionBegin;
4132   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4133   PetscValidPointer(depthLabel, 2);
4134   *depthLabel = dm->depthLabel;
4135   PetscFunctionReturn(0);
4136 }
4137 
4138 /*@
4139   DMPlexGetDepth - Get the depth of the DAG representing this mesh
4140 
4141   Not Collective
4142 
4143   Input Parameter:
4144 . dm    - The DMPlex object
4145 
4146   Output Parameter:
4147 . depth - The number of strata (breadth first levels) in the DAG
4148 
4149   Level: developer
4150 
4151   Notes:
4152   This returns maximum of point depths over all points, i.e. maximum value of the label returned by DMPlexGetDepthLabel().
4153   The point depth is described more in detail in DMPlexGetDepthStratum().
4154   An empty mesh gives -1.
4155 
4156 .seealso: DMPlexGetDepthLabel(), DMPlexGetDepthStratum(), DMPlexGetPointDepth(), DMPlexSymmetrize()
4157 @*/
4158 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
4159 {
4160   DMLabel        label;
4161   PetscInt       d = 0;
4162   PetscErrorCode ierr;
4163 
4164   PetscFunctionBegin;
4165   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4166   PetscValidPointer(depth, 2);
4167   ierr = DMPlexGetDepthLabel(dm, &label);CHKERRQ(ierr);
4168   if (label) {ierr = DMLabelGetNumValues(label, &d);CHKERRQ(ierr);}
4169   *depth = d-1;
4170   PetscFunctionReturn(0);
4171 }
4172 
4173 /*@
4174   DMPlexGetDepthStratum - Get the bounds [start, end) for all points at a certain depth.
4175 
4176   Not Collective
4177 
4178   Input Parameters:
4179 + dm           - The DMPlex object
4180 - stratumValue - The requested depth
4181 
4182   Output Parameters:
4183 + start - The first point at this depth
4184 - end   - One beyond the last point at this depth
4185 
4186   Notes:
4187   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
4188   often "vertices".  If the mesh is "interpolated" (see DMPlexInterpolate()), then depth stratum 1 contains the next
4189   higher dimension, e.g., "edges".
4190 
4191   Level: developer
4192 
4193 .seealso: DMPlexGetHeightStratum(), DMPlexGetDepth(), DMPlexGetDepthLabel(), DMPlexGetPointDepth(), DMPlexSymmetrize(), DMPlexInterpolate()
4194 @*/
4195 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt stratumValue, PetscInt *start, PetscInt *end)
4196 {
4197   DMLabel        label;
4198   PetscInt       pStart, pEnd;
4199   PetscErrorCode ierr;
4200 
4201   PetscFunctionBegin;
4202   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4203   if (start) {PetscValidPointer(start, 3); *start = 0;}
4204   if (end)   {PetscValidPointer(end,   4); *end   = 0;}
4205   ierr = DMPlexGetChart(dm, &pStart, &pEnd);CHKERRQ(ierr);
4206   if (pStart == pEnd) PetscFunctionReturn(0);
4207   if (stratumValue < 0) {
4208     if (start) *start = pStart;
4209     if (end)   *end   = pEnd;
4210     PetscFunctionReturn(0);
4211   }
4212   ierr = DMPlexGetDepthLabel(dm, &label);CHKERRQ(ierr);
4213   if (!label) SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
4214   ierr = DMLabelGetStratumBounds(label, stratumValue, start, end);CHKERRQ(ierr);
4215   PetscFunctionReturn(0);
4216 }
4217 
4218 /*@
4219   DMPlexGetHeightStratum - Get the bounds [start, end) for all points at a certain height.
4220 
4221   Not Collective
4222 
4223   Input Parameters:
4224 + dm           - The DMPlex object
4225 - stratumValue - The requested height
4226 
4227   Output Parameters:
4228 + start - The first point at this height
4229 - end   - One beyond the last point at this height
4230 
4231   Notes:
4232   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
4233   points, often called "cells" or "elements".  If the mesh is "interpolated" (see DMPlexInterpolate()), then height
4234   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
4235 
4236   Level: developer
4237 
4238 .seealso: DMPlexGetDepthStratum(), DMPlexGetDepth(), DMPlexGetPointHeight()
4239 @*/
4240 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt stratumValue, PetscInt *start, PetscInt *end)
4241 {
4242   DMLabel        label;
4243   PetscInt       depth, pStart, pEnd;
4244   PetscErrorCode ierr;
4245 
4246   PetscFunctionBegin;
4247   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4248   if (start) {PetscValidPointer(start, 3); *start = 0;}
4249   if (end)   {PetscValidPointer(end,   4); *end   = 0;}
4250   ierr = DMPlexGetChart(dm, &pStart, &pEnd);CHKERRQ(ierr);
4251   if (pStart == pEnd) PetscFunctionReturn(0);
4252   if (stratumValue < 0) {
4253     if (start) *start = pStart;
4254     if (end)   *end   = pEnd;
4255     PetscFunctionReturn(0);
4256   }
4257   ierr = DMPlexGetDepthLabel(dm, &label);CHKERRQ(ierr);
4258   if (!label) SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
4259   ierr = DMLabelGetNumValues(label, &depth);CHKERRQ(ierr);
4260   ierr = DMLabelGetStratumBounds(label, depth-1-stratumValue, start, end);CHKERRQ(ierr);
4261   PetscFunctionReturn(0);
4262 }
4263 
4264 /*@
4265   DMPlexGetPointDepth - Get the depth of a given point
4266 
4267   Not Collective
4268 
4269   Input Parameter:
4270 + dm    - The DMPlex object
4271 - point - The point
4272 
4273   Output Parameter:
4274 . depth - The depth of the point
4275 
4276   Level: intermediate
4277 
4278 .seealso: DMPlexGetCellType(), DMPlexGetDepthLabel(), DMPlexGetDepth(), DMPlexGetPointHeight()
4279 @*/
4280 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
4281 {
4282   PetscErrorCode ierr;
4283 
4284   PetscFunctionBegin;
4285   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4286   PetscValidIntPointer(depth, 3);
4287   ierr = DMLabelGetValue(dm->depthLabel, point, depth);CHKERRQ(ierr);
4288   PetscFunctionReturn(0);
4289 }
4290 
4291 /*@
4292   DMPlexGetPointHeight - Get the height of a given point
4293 
4294   Not Collective
4295 
4296   Input Parameter:
4297 + dm    - The DMPlex object
4298 - point - The point
4299 
4300   Output Parameter:
4301 . height - The height of the point
4302 
4303   Level: intermediate
4304 
4305 .seealso: DMPlexGetCellType(), DMPlexGetDepthLabel(), DMPlexGetDepth(), DMPlexGetPointDepth()
4306 @*/
4307 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
4308 {
4309   PetscInt       n, pDepth;
4310   PetscErrorCode ierr;
4311 
4312   PetscFunctionBegin;
4313   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4314   PetscValidIntPointer(height, 3);
4315   ierr = DMLabelGetNumValues(dm->depthLabel, &n);CHKERRQ(ierr);
4316   ierr = DMLabelGetValue(dm->depthLabel, point, &pDepth);CHKERRQ(ierr);
4317   *height = n - 1 - pDepth;  /* DAG depth is n-1 */
4318   PetscFunctionReturn(0);
4319 }
4320 
4321 /*@
4322   DMPlexGetCellTypeLabel - Get the DMLabel recording the polytope type of each cell
4323 
4324   Not Collective
4325 
4326   Input Parameter:
4327 . dm - The DMPlex object
4328 
4329   Output Parameter:
4330 . celltypeLabel - The DMLabel recording cell polytope type
4331 
4332   Note: This function will trigger automatica computation of cell types. This can be disabled by calling
4333   DMCreateLabel(dm, "celltype") beforehand.
4334 
4335   Level: developer
4336 
4337 .seealso: DMPlexGetCellType(), DMPlexGetDepthLabel(), DMCreateLabel()
4338 @*/
4339 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
4340 {
4341   PetscErrorCode ierr;
4342 
4343   PetscFunctionBegin;
4344   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4345   PetscValidPointer(celltypeLabel, 2);
4346   if (!dm->celltypeLabel) {ierr = DMPlexComputeCellTypes(dm);CHKERRQ(ierr);}
4347   *celltypeLabel = dm->celltypeLabel;
4348   PetscFunctionReturn(0);
4349 }
4350 
4351 /*@
4352   DMPlexGetCellType - Get the polytope type of a given cell
4353 
4354   Not Collective
4355 
4356   Input Parameter:
4357 + dm   - The DMPlex object
4358 - cell - The cell
4359 
4360   Output Parameter:
4361 . celltype - The polytope type of the cell
4362 
4363   Level: intermediate
4364 
4365 .seealso: DMPlexGetCellTypeLabel(), DMPlexGetDepthLabel(), DMPlexGetDepth()
4366 @*/
4367 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
4368 {
4369   DMLabel        label;
4370   PetscInt       ct;
4371   PetscErrorCode ierr;
4372 
4373   PetscFunctionBegin;
4374   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4375   PetscValidPointer(celltype, 3);
4376   ierr = DMPlexGetCellTypeLabel(dm, &label);CHKERRQ(ierr);
4377   ierr = DMLabelGetValue(label, cell, &ct);CHKERRQ(ierr);
4378   if (ct < 0) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %D has not been assigned a cell type", cell);
4379   *celltype = (DMPolytopeType) ct;
4380   PetscFunctionReturn(0);
4381 }
4382 
4383 /*@
4384   DMPlexSetCellType - Set the polytope type of a given cell
4385 
4386   Not Collective
4387 
4388   Input Parameters:
4389 + dm   - The DMPlex object
4390 . cell - The cell
4391 - celltype - The polytope type of the cell
4392 
4393   Note: By default, cell types will be automatically computed using DMPlexComputeCellTypes() before this function
4394   is executed. This function will override the computed type. However, if automatic classification will not succeed
4395   and a user wants to manually specify all types, the classification must be disabled by calling
4396   DMCreaateLabel(dm, "celltype") before getting or setting any cell types.
4397 
4398   Level: advanced
4399 
4400 .seealso: DMPlexGetCellTypeLabel(), DMPlexGetDepthLabel(), DMPlexGetDepth(), DMPlexComputeCellTypes(), DMCreateLabel()
4401 @*/
4402 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
4403 {
4404   DMLabel        label;
4405   PetscErrorCode ierr;
4406 
4407   PetscFunctionBegin;
4408   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4409   ierr = DMPlexGetCellTypeLabel(dm, &label);CHKERRQ(ierr);
4410   ierr = DMLabelSetValue(label, cell, celltype);CHKERRQ(ierr);
4411   PetscFunctionReturn(0);
4412 }
4413 
4414 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
4415 {
4416   PetscSection   section, s;
4417   Mat            m;
4418   PetscInt       maxHeight;
4419   PetscErrorCode ierr;
4420 
4421   PetscFunctionBegin;
4422   ierr = DMClone(dm, cdm);CHKERRQ(ierr);
4423   ierr = DMPlexGetMaxProjectionHeight(dm, &maxHeight);CHKERRQ(ierr);
4424   ierr = DMPlexSetMaxProjectionHeight(*cdm, maxHeight);CHKERRQ(ierr);
4425   ierr = PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section);CHKERRQ(ierr);
4426   ierr = DMSetLocalSection(*cdm, section);CHKERRQ(ierr);
4427   ierr = PetscSectionDestroy(&section);CHKERRQ(ierr);
4428   ierr = PetscSectionCreate(PETSC_COMM_SELF, &s);CHKERRQ(ierr);
4429   ierr = MatCreate(PETSC_COMM_SELF, &m);CHKERRQ(ierr);
4430   ierr = DMSetDefaultConstraints(*cdm, s, m);CHKERRQ(ierr);
4431   ierr = PetscSectionDestroy(&s);CHKERRQ(ierr);
4432   ierr = MatDestroy(&m);CHKERRQ(ierr);
4433 
4434   ierr = DMSetNumFields(*cdm, 1);CHKERRQ(ierr);
4435   ierr = DMCreateDS(*cdm);CHKERRQ(ierr);
4436   PetscFunctionReturn(0);
4437 }
4438 
4439 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
4440 {
4441   Vec            coordsLocal;
4442   DM             coordsDM;
4443   PetscErrorCode ierr;
4444 
4445   PetscFunctionBegin;
4446   *field = NULL;
4447   ierr = DMGetCoordinatesLocal(dm,&coordsLocal);CHKERRQ(ierr);
4448   ierr = DMGetCoordinateDM(dm,&coordsDM);CHKERRQ(ierr);
4449   if (coordsLocal && coordsDM) {
4450     ierr = DMFieldCreateDS(coordsDM, 0, coordsLocal, field);CHKERRQ(ierr);
4451   }
4452   PetscFunctionReturn(0);
4453 }
4454 
4455 /*@C
4456   DMPlexGetConeSection - Return a section which describes the layout of cone data
4457 
4458   Not Collective
4459 
4460   Input Parameters:
4461 . dm        - The DMPlex object
4462 
4463   Output Parameter:
4464 . section - The PetscSection object
4465 
4466   Level: developer
4467 
4468 .seealso: DMPlexGetSupportSection(), DMPlexGetCones(), DMPlexGetConeOrientations()
4469 @*/
4470 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
4471 {
4472   DM_Plex *mesh = (DM_Plex*) dm->data;
4473 
4474   PetscFunctionBegin;
4475   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4476   if (section) *section = mesh->coneSection;
4477   PetscFunctionReturn(0);
4478 }
4479 
4480 /*@C
4481   DMPlexGetSupportSection - Return a section which describes the layout of support data
4482 
4483   Not Collective
4484 
4485   Input Parameters:
4486 . dm        - The DMPlex object
4487 
4488   Output Parameter:
4489 . section - The PetscSection object
4490 
4491   Level: developer
4492 
4493 .seealso: DMPlexGetConeSection()
4494 @*/
4495 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
4496 {
4497   DM_Plex *mesh = (DM_Plex*) dm->data;
4498 
4499   PetscFunctionBegin;
4500   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4501   if (section) *section = mesh->supportSection;
4502   PetscFunctionReturn(0);
4503 }
4504 
4505 /*@C
4506   DMPlexGetCones - Return cone data
4507 
4508   Not Collective
4509 
4510   Input Parameters:
4511 . dm        - The DMPlex object
4512 
4513   Output Parameter:
4514 . cones - The cone for each point
4515 
4516   Level: developer
4517 
4518 .seealso: DMPlexGetConeSection()
4519 @*/
4520 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
4521 {
4522   DM_Plex *mesh = (DM_Plex*) dm->data;
4523 
4524   PetscFunctionBegin;
4525   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4526   if (cones) *cones = mesh->cones;
4527   PetscFunctionReturn(0);
4528 }
4529 
4530 /*@C
4531   DMPlexGetConeOrientations - Return cone orientation data
4532 
4533   Not Collective
4534 
4535   Input Parameters:
4536 . dm        - The DMPlex object
4537 
4538   Output Parameter:
4539 . coneOrientations - The cone orientation for each point
4540 
4541   Level: developer
4542 
4543 .seealso: DMPlexGetConeSection()
4544 @*/
4545 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
4546 {
4547   DM_Plex *mesh = (DM_Plex*) dm->data;
4548 
4549   PetscFunctionBegin;
4550   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4551   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
4552   PetscFunctionReturn(0);
4553 }
4554 
4555 /******************************** FEM Support **********************************/
4556 
4557 /*
4558  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
4559  representing a line in the section.
4560 */
4561 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(PetscSection section,PetscInt field,PetscInt line,PetscBool vertexchart,PetscInt *Nc,PetscInt *k)
4562 {
4563   PetscErrorCode ierr;
4564 
4565   PetscFunctionBeginHot;
4566   ierr = PetscSectionGetFieldComponents(section, field, Nc);CHKERRQ(ierr);
4567   if (line < 0) {
4568     *k = 0;
4569     *Nc = 0;
4570   } else if (vertexchart) {            /* If we only have a vertex chart, we must have degree k=1 */
4571     *k = 1;
4572   } else {                      /* Assume the full interpolated mesh is in the chart; lines in particular */
4573     /* An order k SEM disc has k-1 dofs on an edge */
4574     ierr = PetscSectionGetFieldDof(section, line, field, k);CHKERRQ(ierr);
4575     *k = *k / *Nc + 1;
4576   }
4577   PetscFunctionReturn(0);
4578 }
4579 
4580 /*@
4581 
4582   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
4583   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
4584   section provided (or the section of the DM).
4585 
4586   Input Parameters:
4587 + dm      - The DM
4588 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or PETSC_DETERMINE
4589 - section - The PetscSection to reorder, or NULL for the default section
4590 
4591   Note: The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
4592   degree of the basis.
4593 
4594   Example:
4595   A typical interpolated single-quad mesh might order points as
4596 .vb
4597   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
4598 
4599   v4 -- e6 -- v3
4600   |           |
4601   e7    c0    e8
4602   |           |
4603   v1 -- e5 -- v2
4604 .ve
4605 
4606   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
4607   dofs in the order of points, e.g.,
4608 .vb
4609     c0 -> [0,1,2,3]
4610     v1 -> [4]
4611     ...
4612     e5 -> [8, 9]
4613 .ve
4614 
4615   which corresponds to the dofs
4616 .vb
4617     6   10  11  7
4618     13  2   3   15
4619     12  0   1   14
4620     4   8   9   5
4621 .ve
4622 
4623   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
4624 .vb
4625   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
4626 .ve
4627 
4628   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
4629 .vb
4630    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
4631 .ve
4632 
4633   Level: developer
4634 
4635 .seealso: DMGetLocalSection(), PetscSectionSetClosurePermutation(), DMSetGlobalSection()
4636 @*/
4637 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
4638 {
4639   DMLabel        label;
4640   PetscInt       dim, depth = -1, eStart = -1, Nf;
4641   PetscBool      vertexchart;
4642   PetscErrorCode ierr;
4643 
4644   PetscFunctionBegin;
4645   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
4646   if (dim < 1) PetscFunctionReturn(0);
4647   if (point < 0) {
4648     PetscInt sStart,sEnd;
4649 
4650     ierr = DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd);CHKERRQ(ierr);
4651     point = sEnd-sStart ? sStart : point;
4652   }
4653   ierr = DMPlexGetDepthLabel(dm, &label);CHKERRQ(ierr);
4654   if (point >= 0) { ierr = DMLabelGetValue(label, point, &depth);CHKERRQ(ierr); }
4655   if (!section) {ierr = DMGetLocalSection(dm, &section);CHKERRQ(ierr);}
4656   if (depth == 1) {eStart = point;}
4657   else if  (depth == dim) {
4658     const PetscInt *cone;
4659 
4660     ierr = DMPlexGetCone(dm, point, &cone);CHKERRQ(ierr);
4661     if (dim == 2) eStart = cone[0];
4662     else if (dim == 3) {
4663       const PetscInt *cone2;
4664       ierr = DMPlexGetCone(dm, cone[0], &cone2);CHKERRQ(ierr);
4665       eStart = cone2[0];
4666     } 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);
4667   } 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);
4668   {                             /* Determine whether the chart covers all points or just vertices. */
4669     PetscInt pStart,pEnd,cStart,cEnd;
4670     ierr = DMPlexGetDepthStratum(dm,0,&pStart,&pEnd);CHKERRQ(ierr);
4671     ierr = PetscSectionGetChart(section,&cStart,&cEnd);CHKERRQ(ierr);
4672     if (pStart == cStart && pEnd == cEnd) vertexchart = PETSC_TRUE; /* Just vertices */
4673     else vertexchart = PETSC_FALSE;                                 /* Assume all interpolated points are in chart */
4674   }
4675   ierr = PetscSectionGetNumFields(section, &Nf);CHKERRQ(ierr);
4676   for (PetscInt d=1; d<=dim; d++) {
4677     PetscInt k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
4678     PetscInt *perm;
4679 
4680     for (f = 0; f < Nf; ++f) {
4681       ierr = PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k);CHKERRQ(ierr);
4682       size += PetscPowInt(k+1, d)*Nc;
4683     }
4684     ierr = PetscMalloc1(size, &perm);CHKERRQ(ierr);
4685     for (f = 0; f < Nf; ++f) {
4686       switch (d) {
4687       case 1:
4688         ierr = PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k);CHKERRQ(ierr);
4689         /*
4690          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
4691          We want              [ vtx0; edge of length k-1; vtx1 ]
4692          */
4693         for (c=0; c<Nc; c++,offset++) perm[offset] = (k-1)*Nc + c + foffset;
4694         for (i=0; i<k-1; i++) for (c=0; c<Nc; c++,offset++) perm[offset] = i*Nc + c + foffset;
4695         for (c=0; c<Nc; c++,offset++) perm[offset] = k*Nc + c + foffset;
4696         foffset = offset;
4697         break;
4698       case 2:
4699         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
4700         ierr = PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k);CHKERRQ(ierr);
4701         /* The SEM order is
4702 
4703          v_lb, {e_b}, v_rb,
4704          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
4705          v_lt, reverse {e_t}, v_rt
4706          */
4707         {
4708           const PetscInt of   = 0;
4709           const PetscInt oeb  = of   + PetscSqr(k-1);
4710           const PetscInt oer  = oeb  + (k-1);
4711           const PetscInt oet  = oer  + (k-1);
4712           const PetscInt oel  = oet  + (k-1);
4713           const PetscInt ovlb = oel  + (k-1);
4714           const PetscInt ovrb = ovlb + 1;
4715           const PetscInt ovrt = ovrb + 1;
4716           const PetscInt ovlt = ovrt + 1;
4717           PetscInt       o;
4718 
4719           /* bottom */
4720           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb*Nc + c + foffset;
4721           for (o = oeb; o < oer; ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
4722           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb*Nc + c + foffset;
4723           /* middle */
4724           for (i = 0; i < k-1; ++i) {
4725             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel+(k-2)-i)*Nc + c + foffset;
4726             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;
4727             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer+i)*Nc + c + foffset;
4728           }
4729           /* top */
4730           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt*Nc + c + foffset;
4731           for (o = oel-1; o >= oet; --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
4732           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt*Nc + c + foffset;
4733           foffset = offset;
4734         }
4735         break;
4736       case 3:
4737         /* The original hex closure is
4738 
4739          {c,
4740          f_b, f_t, f_f, f_b, f_r, f_l,
4741          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
4742          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
4743          */
4744         ierr = PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k);CHKERRQ(ierr);
4745         /* The SEM order is
4746          Bottom Slice
4747          v_blf, {e^{(k-1)-n}_bf}, v_brf,
4748          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
4749          v_blb, {e_bb}, v_brb,
4750 
4751          Middle Slice (j)
4752          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
4753          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
4754          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
4755 
4756          Top Slice
4757          v_tlf, {e_tf}, v_trf,
4758          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
4759          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
4760          */
4761         {
4762           const PetscInt oc    = 0;
4763           const PetscInt ofb   = oc    + PetscSqr(k-1)*(k-1);
4764           const PetscInt oft   = ofb   + PetscSqr(k-1);
4765           const PetscInt off   = oft   + PetscSqr(k-1);
4766           const PetscInt ofk   = off   + PetscSqr(k-1);
4767           const PetscInt ofr   = ofk   + PetscSqr(k-1);
4768           const PetscInt ofl   = ofr   + PetscSqr(k-1);
4769           const PetscInt oebl  = ofl   + PetscSqr(k-1);
4770           const PetscInt oebb  = oebl  + (k-1);
4771           const PetscInt oebr  = oebb  + (k-1);
4772           const PetscInt oebf  = oebr  + (k-1);
4773           const PetscInt oetf  = oebf  + (k-1);
4774           const PetscInt oetr  = oetf  + (k-1);
4775           const PetscInt oetb  = oetr  + (k-1);
4776           const PetscInt oetl  = oetb  + (k-1);
4777           const PetscInt oerf  = oetl  + (k-1);
4778           const PetscInt oelf  = oerf  + (k-1);
4779           const PetscInt oelb  = oelf  + (k-1);
4780           const PetscInt oerb  = oelb  + (k-1);
4781           const PetscInt ovblf = oerb  + (k-1);
4782           const PetscInt ovblb = ovblf + 1;
4783           const PetscInt ovbrb = ovblb + 1;
4784           const PetscInt ovbrf = ovbrb + 1;
4785           const PetscInt ovtlf = ovbrf + 1;
4786           const PetscInt ovtrf = ovtlf + 1;
4787           const PetscInt ovtrb = ovtrf + 1;
4788           const PetscInt ovtlb = ovtrb + 1;
4789           PetscInt       o, n;
4790 
4791           /* Bottom Slice */
4792           /*   bottom */
4793           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf*Nc + c + foffset;
4794           for (o = oetf-1; o >= oebf; --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
4795           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf*Nc + c + foffset;
4796           /*   middle */
4797           for (i = 0; i < k-1; ++i) {
4798             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl+i)*Nc + c + foffset;
4799             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;}
4800             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr+(k-2)-i)*Nc + c + foffset;
4801           }
4802           /*   top */
4803           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb*Nc + c + foffset;
4804           for (o = oebb; o < oebr; ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
4805           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb*Nc + c + foffset;
4806 
4807           /* Middle Slice */
4808           for (j = 0; j < k-1; ++j) {
4809             /*   bottom */
4810             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf+(k-2)-j)*Nc + c + foffset;
4811             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;
4812             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf+j)*Nc + c + foffset;
4813             /*   middle */
4814             for (i = 0; i < k-1; ++i) {
4815               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl+i*(k-1)+j)*Nc + c + foffset;
4816               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;
4817               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr+j*(k-1)+i)*Nc + c + foffset;
4818             }
4819             /*   top */
4820             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb+j)*Nc + c + foffset;
4821             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;
4822             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb+(k-2)-j)*Nc + c + foffset;
4823           }
4824 
4825           /* Top Slice */
4826           /*   bottom */
4827           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf*Nc + c + foffset;
4828           for (o = oetf; o < oetr; ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
4829           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf*Nc + c + foffset;
4830           /*   middle */
4831           for (i = 0; i < k-1; ++i) {
4832             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl+(k-2)-i)*Nc + c + foffset;
4833             for (n = 0; n < k-1; ++n) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft+i*(k-1)+n)*Nc + c + foffset;
4834             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr+i)*Nc + c + foffset;
4835           }
4836           /*   top */
4837           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb*Nc + c + foffset;
4838           for (o = oetl-1; o >= oetb; --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
4839           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb*Nc + c + foffset;
4840 
4841           foffset = offset;
4842         }
4843         break;
4844       default: SETERRQ1(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %D", d);
4845       }
4846     }
4847     if (offset != size) SETERRQ2(PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Number of permutation entries %D != %D", offset, size);
4848     /* Check permutation */
4849     {
4850       PetscInt *check;
4851 
4852       ierr = PetscMalloc1(size, &check);CHKERRQ(ierr);
4853       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]);}
4854       for (i = 0; i < size; ++i) check[perm[i]] = i;
4855       for (i = 0; i < size; ++i) {if (check[i] < 0) SETERRQ1(PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Missing permutation index %D", i);}
4856       ierr = PetscFree(check);CHKERRQ(ierr);
4857     }
4858     ierr = PetscSectionSetClosurePermutation_Internal(section, (PetscObject) dm, d, size, PETSC_OWN_POINTER, perm);CHKERRQ(ierr);
4859   }
4860   PetscFunctionReturn(0);
4861 }
4862 
4863 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
4864 {
4865   PetscDS        prob;
4866   PetscInt       depth, Nf, h;
4867   DMLabel        label;
4868   PetscErrorCode ierr;
4869 
4870   PetscFunctionBeginHot;
4871   ierr = DMGetDS(dm, &prob);CHKERRQ(ierr);
4872   Nf      = prob->Nf;
4873   label   = dm->depthLabel;
4874   *dspace = NULL;
4875   if (field < Nf) {
4876     PetscObject disc = prob->disc[field];
4877 
4878     if (disc->classid == PETSCFE_CLASSID) {
4879       PetscDualSpace dsp;
4880 
4881       ierr = PetscFEGetDualSpace((PetscFE)disc,&dsp);CHKERRQ(ierr);
4882       ierr = DMLabelGetNumValues(label,&depth);CHKERRQ(ierr);
4883       ierr = DMLabelGetValue(label,point,&h);CHKERRQ(ierr);
4884       h    = depth - 1 - h;
4885       if (h) {
4886         ierr = PetscDualSpaceGetHeightSubspace(dsp,h,dspace);CHKERRQ(ierr);
4887       } else {
4888         *dspace = dsp;
4889       }
4890     }
4891   }
4892   PetscFunctionReturn(0);
4893 }
4894 
4895 PETSC_STATIC_INLINE PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
4896 {
4897   PetscScalar    *array, *vArray;
4898   const PetscInt *cone, *coneO;
4899   PetscInt        pStart, pEnd, p, numPoints, size = 0, offset = 0;
4900   PetscErrorCode  ierr;
4901 
4902   PetscFunctionBeginHot;
4903   ierr = PetscSectionGetChart(section, &pStart, &pEnd);CHKERRQ(ierr);
4904   ierr = DMPlexGetConeSize(dm, point, &numPoints);CHKERRQ(ierr);
4905   ierr = DMPlexGetCone(dm, point, &cone);CHKERRQ(ierr);
4906   ierr = DMPlexGetConeOrientation(dm, point, &coneO);CHKERRQ(ierr);
4907   if (!values || !*values) {
4908     if ((point >= pStart) && (point < pEnd)) {
4909       PetscInt dof;
4910 
4911       ierr = PetscSectionGetDof(section, point, &dof);CHKERRQ(ierr);
4912       size += dof;
4913     }
4914     for (p = 0; p < numPoints; ++p) {
4915       const PetscInt cp = cone[p];
4916       PetscInt       dof;
4917 
4918       if ((cp < pStart) || (cp >= pEnd)) continue;
4919       ierr = PetscSectionGetDof(section, cp, &dof);CHKERRQ(ierr);
4920       size += dof;
4921     }
4922     if (!values) {
4923       if (csize) *csize = size;
4924       PetscFunctionReturn(0);
4925     }
4926     ierr = DMGetWorkArray(dm, size, MPIU_SCALAR, &array);CHKERRQ(ierr);
4927   } else {
4928     array = *values;
4929   }
4930   size = 0;
4931   ierr = VecGetArray(v, &vArray);CHKERRQ(ierr);
4932   if ((point >= pStart) && (point < pEnd)) {
4933     PetscInt     dof, off, d;
4934     PetscScalar *varr;
4935 
4936     ierr = PetscSectionGetDof(section, point, &dof);CHKERRQ(ierr);
4937     ierr = PetscSectionGetOffset(section, point, &off);CHKERRQ(ierr);
4938     varr = &vArray[off];
4939     for (d = 0; d < dof; ++d, ++offset) {
4940       array[offset] = varr[d];
4941     }
4942     size += dof;
4943   }
4944   for (p = 0; p < numPoints; ++p) {
4945     const PetscInt cp = cone[p];
4946     PetscInt       o  = coneO[p];
4947     PetscInt       dof, off, d;
4948     PetscScalar   *varr;
4949 
4950     if ((cp < pStart) || (cp >= pEnd)) continue;
4951     ierr = PetscSectionGetDof(section, cp, &dof);CHKERRQ(ierr);
4952     ierr = PetscSectionGetOffset(section, cp, &off);CHKERRQ(ierr);
4953     varr = &vArray[off];
4954     if (o >= 0) {
4955       for (d = 0; d < dof; ++d, ++offset) {
4956         array[offset] = varr[d];
4957       }
4958     } else {
4959       for (d = dof-1; d >= 0; --d, ++offset) {
4960         array[offset] = varr[d];
4961       }
4962     }
4963     size += dof;
4964   }
4965   ierr = VecRestoreArray(v, &vArray);CHKERRQ(ierr);
4966   if (!*values) {
4967     if (csize) *csize = size;
4968     *values = array;
4969   } else {
4970     if (size > *csize) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %D < actual size %D", *csize, size);
4971     *csize = size;
4972   }
4973   PetscFunctionReturn(0);
4974 }
4975 
4976 /* Compress out points not in the section */
4977 PETSC_STATIC_INLINE PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
4978 {
4979   const PetscInt np = *numPoints;
4980   PetscInt       pStart, pEnd, p, q;
4981   PetscErrorCode ierr;
4982 
4983   ierr = PetscSectionGetChart(section, &pStart, &pEnd);CHKERRQ(ierr);
4984   for (p = 0, q = 0; p < np; ++p) {
4985     const PetscInt r = points[p*2];
4986     if ((r >= pStart) && (r < pEnd)) {
4987       points[q*2]   = r;
4988       points[q*2+1] = points[p*2+1];
4989       ++q;
4990     }
4991   }
4992   *numPoints = q;
4993   return 0;
4994 }
4995 
4996 static PetscErrorCode DMPlexTransitiveClosure_Hybrid_Internal(DM dm, PetscInt point, PetscInt np, PetscInt *numPoints, PetscInt **points)
4997 {
4998   const PetscInt *cone, *ornt;
4999   PetscInt       *pts,  *closure = NULL;
5000   PetscInt        dim, coneSize, c, d, clSize, cl;
5001   PetscErrorCode  ierr;
5002 
5003   PetscFunctionBeginHot;
5004   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
5005   ierr = DMPlexGetConeSize(dm, point, &coneSize);CHKERRQ(ierr);
5006   ierr = DMPlexGetCone(dm, point, &cone);CHKERRQ(ierr);
5007   ierr = DMPlexGetConeOrientation(dm, point, &ornt);CHKERRQ(ierr);
5008   ierr = DMPlexGetTransitiveClosure(dm, cone[0], PETSC_TRUE, &clSize, &closure);CHKERRQ(ierr);
5009   ierr = DMGetWorkArray(dm, np*2, MPIU_INT, &pts);CHKERRQ(ierr);
5010   c    = 0;
5011   pts[c*2+0] = point;
5012   pts[c*2+1] = 0;
5013   ++c;
5014   for (cl = 0; cl < clSize*2; cl += 2, ++c) {pts[c*2+0] = closure[cl]; pts[c*2+1] = closure[cl+1];}
5015   ierr = DMPlexGetTransitiveClosure(dm, cone[1], PETSC_TRUE, &clSize, &closure);CHKERRQ(ierr);
5016   for (cl = 0; cl < clSize*2; cl += 2, ++c) {pts[c*2+0] = closure[cl]; pts[c*2+1] = closure[cl+1];}
5017   ierr = DMPlexRestoreTransitiveClosure(dm, cone[0], PETSC_TRUE, &clSize, &closure);CHKERRQ(ierr);
5018   if (dim >= 2) {
5019     for (d = 2; d < coneSize; ++d, ++c) {pts[c*2+0] = cone[d]; pts[c*2+1] = ornt[d];}
5020   }
5021   if (dim >= 3) {
5022     for (d = 2; d < coneSize; ++d) {
5023       const PetscInt  fpoint = cone[d];
5024       const PetscInt *fcone;
5025       PetscInt        fconeSize, fc, i;
5026 
5027       ierr = DMPlexGetConeSize(dm, fpoint, &fconeSize);CHKERRQ(ierr);
5028       ierr = DMPlexGetCone(dm, fpoint, &fcone);CHKERRQ(ierr);
5029       for (fc = 0; fc < fconeSize; ++fc) {
5030         for (i = 0; i < c; ++i) if (pts[i*2] == fcone[fc]) break;
5031         if (i == c) {pts[c*2+0] = fcone[fc]; pts[c*2+1] = 0; ++c;}
5032       }
5033     }
5034   }
5035   if (c != np) SETERRQ3(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid closure for hybrid point %D, size %D != %D", point, c, np);
5036   *numPoints = np;
5037   *points    = pts;
5038   PetscFunctionReturn(0);
5039 }
5040 
5041 /* Compressed closure does not apply closure permutation */
5042 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5043 {
5044   const PetscInt *cla = NULL;
5045   PetscInt       np, *pts = NULL;
5046   PetscErrorCode ierr;
5047 
5048   PetscFunctionBeginHot;
5049   ierr = PetscSectionGetClosureIndex(section, (PetscObject) dm, clSec, clPoints);CHKERRQ(ierr);
5050   if (*clPoints) {
5051     PetscInt dof, off;
5052 
5053     ierr = PetscSectionGetDof(*clSec, point, &dof);CHKERRQ(ierr);
5054     ierr = PetscSectionGetOffset(*clSec, point, &off);CHKERRQ(ierr);
5055     ierr = ISGetIndices(*clPoints, &cla);CHKERRQ(ierr);
5056     np   = dof/2;
5057     pts  = (PetscInt *) &cla[off];
5058   } else {
5059     DMPolytopeType ct;
5060 
5061     /* Do not make the label if it does not exist */
5062     if (!dm->celltypeLabel) {ct = DM_POLYTOPE_POINT;}
5063     else                    {ierr = DMPlexGetCellType(dm, point, &ct);CHKERRQ(ierr);}
5064     switch (ct) {
5065       case DM_POLYTOPE_SEG_PRISM_TENSOR:
5066         ierr = DMPlexTransitiveClosure_Hybrid_Internal(dm, point, 9, &np, &pts);CHKERRQ(ierr);
5067         break;
5068       case DM_POLYTOPE_TRI_PRISM_TENSOR:
5069         ierr = DMPlexTransitiveClosure_Hybrid_Internal(dm, point, 21, &np, &pts);CHKERRQ(ierr);
5070         break;
5071       case DM_POLYTOPE_QUAD_PRISM_TENSOR:
5072         ierr = DMPlexTransitiveClosure_Hybrid_Internal(dm, point, 27, &np, &pts);CHKERRQ(ierr);
5073         break;
5074       default:
5075         ierr = DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &np, &pts);CHKERRQ(ierr);
5076     }
5077     ierr = CompressPoints_Private(section, &np, pts);CHKERRQ(ierr);
5078   }
5079   *numPoints = np;
5080   *points    = pts;
5081   *clp       = cla;
5082   PetscFunctionReturn(0);
5083 }
5084 
5085 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5086 {
5087   PetscErrorCode ierr;
5088 
5089   PetscFunctionBeginHot;
5090   if (!*clPoints) {
5091     ierr = DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points);CHKERRQ(ierr);
5092   } else {
5093     ierr = ISRestoreIndices(*clPoints, clp);CHKERRQ(ierr);
5094   }
5095   *numPoints = 0;
5096   *points    = NULL;
5097   *clSec     = NULL;
5098   *clPoints  = NULL;
5099   *clp       = NULL;
5100   PetscFunctionReturn(0);
5101 }
5102 
5103 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[])
5104 {
5105   PetscInt          offset = 0, p;
5106   const PetscInt    **perms = NULL;
5107   const PetscScalar **flips = NULL;
5108   PetscErrorCode    ierr;
5109 
5110   PetscFunctionBeginHot;
5111   *size = 0;
5112   ierr = PetscSectionGetPointSyms(section,numPoints,points,&perms,&flips);CHKERRQ(ierr);
5113   for (p = 0; p < numPoints; p++) {
5114     const PetscInt    point = points[2*p];
5115     const PetscInt    *perm = perms ? perms[p] : NULL;
5116     const PetscScalar *flip = flips ? flips[p] : NULL;
5117     PetscInt          dof, off, d;
5118     const PetscScalar *varr;
5119 
5120     ierr = PetscSectionGetDof(section, point, &dof);CHKERRQ(ierr);
5121     ierr = PetscSectionGetOffset(section, point, &off);CHKERRQ(ierr);
5122     varr = &vArray[off];
5123     if (clperm) {
5124       if (perm) {
5125         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]]  = varr[d];
5126       } else {
5127         for (d = 0; d < dof; d++) array[clperm[offset +      d ]]  = varr[d];
5128       }
5129       if (flip) {
5130         for (d = 0; d < dof; d++) array[clperm[offset +      d ]] *= flip[d];
5131       }
5132     } else {
5133       if (perm) {
5134         for (d = 0; d < dof; d++) array[offset + perm[d]]  = varr[d];
5135       } else {
5136         for (d = 0; d < dof; d++) array[offset +      d ]  = varr[d];
5137       }
5138       if (flip) {
5139         for (d = 0; d < dof; d++) array[offset +      d ] *= flip[d];
5140       }
5141     }
5142     offset += dof;
5143   }
5144   ierr = PetscSectionRestorePointSyms(section,numPoints,points,&perms,&flips);CHKERRQ(ierr);
5145   *size = offset;
5146   PetscFunctionReturn(0);
5147 }
5148 
5149 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[])
5150 {
5151   PetscInt          offset = 0, f;
5152   PetscErrorCode    ierr;
5153 
5154   PetscFunctionBeginHot;
5155   *size = 0;
5156   for (f = 0; f < numFields; ++f) {
5157     PetscInt          p;
5158     const PetscInt    **perms = NULL;
5159     const PetscScalar **flips = NULL;
5160 
5161     ierr = PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips);CHKERRQ(ierr);
5162     for (p = 0; p < numPoints; p++) {
5163       const PetscInt    point = points[2*p];
5164       PetscInt          fdof, foff, b;
5165       const PetscScalar *varr;
5166       const PetscInt    *perm = perms ? perms[p] : NULL;
5167       const PetscScalar *flip = flips ? flips[p] : NULL;
5168 
5169       ierr = PetscSectionGetFieldDof(section, point, f, &fdof);CHKERRQ(ierr);
5170       ierr = PetscSectionGetFieldOffset(section, point, f, &foff);CHKERRQ(ierr);
5171       varr = &vArray[foff];
5172       if (clperm) {
5173         if (perm) {for (b = 0; b < fdof; b++) {array[clperm[offset + perm[b]]]  = varr[b];}}
5174         else      {for (b = 0; b < fdof; b++) {array[clperm[offset +      b ]]  = varr[b];}}
5175         if (flip) {for (b = 0; b < fdof; b++) {array[clperm[offset +      b ]] *= flip[b];}}
5176       } else {
5177         if (perm) {for (b = 0; b < fdof; b++) {array[offset + perm[b]]  = varr[b];}}
5178         else      {for (b = 0; b < fdof; b++) {array[offset +      b ]  = varr[b];}}
5179         if (flip) {for (b = 0; b < fdof; b++) {array[offset +      b ] *= flip[b];}}
5180       }
5181       offset += fdof;
5182     }
5183     ierr = PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips);CHKERRQ(ierr);
5184   }
5185   *size = offset;
5186   PetscFunctionReturn(0);
5187 }
5188 
5189 /*@C
5190   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
5191 
5192   Not collective
5193 
5194   Input Parameters:
5195 + dm - The DM
5196 . section - The section describing the layout in v, or NULL to use the default section
5197 . v - The local vector
5198 . point - The point in the DM
5199 . csize - The size of the input values array, or NULL
5200 - values - An array to use for the values, or NULL to have it allocated automatically
5201 
5202   Output Parameters:
5203 + csize - The number of values in the closure
5204 - values - The array of values. If the user provided NULL, it is a borrowed array and should not be freed
5205 
5206 $ Note that DMPlexVecGetClosure/DMPlexVecRestoreClosure only allocates the values array if it set to NULL in the
5207 $ calling function. This is because DMPlexVecGetClosure() is typically called in the inner loop of a Vec or Mat
5208 $ assembly function, and a user may already have allocated storage for this operation.
5209 $
5210 $ A typical use could be
5211 $
5212 $  values = NULL;
5213 $  ierr = DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values);CHKERRQ(ierr);
5214 $  for (cl = 0; cl < clSize; ++cl) {
5215 $    <Compute on closure>
5216 $  }
5217 $  ierr = DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values);CHKERRQ(ierr);
5218 $
5219 $ or
5220 $
5221 $  PetscMalloc1(clMaxSize, &values);
5222 $  for (p = pStart; p < pEnd; ++p) {
5223 $    clSize = clMaxSize;
5224 $    ierr = DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values);CHKERRQ(ierr);
5225 $    for (cl = 0; cl < clSize; ++cl) {
5226 $      <Compute on closure>
5227 $    }
5228 $  }
5229 $  PetscFree(values);
5230 
5231   Fortran Notes:
5232   Since it returns an array, this routine is only available in Fortran 90, and you must
5233   include petsc.h90 in your code.
5234 
5235   The csize argument is not present in the Fortran 90 binding since it is internal to the array.
5236 
5237   Level: intermediate
5238 
5239 .seealso DMPlexVecRestoreClosure(), DMPlexVecSetClosure(), DMPlexMatSetClosure()
5240 @*/
5241 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5242 {
5243   PetscSection       clSection;
5244   IS                 clPoints;
5245   PetscInt          *points = NULL;
5246   const PetscInt    *clp, *perm;
5247   PetscInt           depth, numFields, numPoints, asize;
5248   PetscErrorCode     ierr;
5249 
5250   PetscFunctionBeginHot;
5251   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5252   if (!section) {ierr = DMGetLocalSection(dm, &section);CHKERRQ(ierr);}
5253   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5254   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5255   ierr = DMPlexGetDepth(dm, &depth);CHKERRQ(ierr);
5256   ierr = PetscSectionGetNumFields(section, &numFields);CHKERRQ(ierr);
5257   if (depth == 1 && numFields < 2) {
5258     ierr = DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values);CHKERRQ(ierr);
5259     PetscFunctionReturn(0);
5260   }
5261   /* Get points */
5262   ierr = DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp);CHKERRQ(ierr);
5263   /* Get sizes */
5264   asize = 0;
5265   for (PetscInt p = 0; p < numPoints*2; p += 2) {
5266     PetscInt dof;
5267     ierr = PetscSectionGetDof(section, points[p], &dof);CHKERRQ(ierr);
5268     asize += dof;
5269   }
5270   if (values) {
5271     const PetscScalar *vArray;
5272     PetscInt          size;
5273 
5274     if (*values) {
5275       if (PetscUnlikely(*csize < asize)) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Provided array size %D not sufficient to hold closure size %D", *csize, asize);
5276     } else {ierr = DMGetWorkArray(dm, asize, MPIU_SCALAR, values);CHKERRQ(ierr);}
5277     ierr = PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, asize, &perm);CHKERRQ(ierr);
5278     ierr = VecGetArrayRead(v, &vArray);CHKERRQ(ierr);
5279     /* Get values */
5280     if (numFields > 0) {ierr = DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values);CHKERRQ(ierr);}
5281     else               {ierr = DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values);CHKERRQ(ierr);}
5282     if (PetscUnlikely(asize != size)) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %D does not match Vec closure size %D", asize, size);
5283     /* Cleanup array */
5284     ierr = VecRestoreArrayRead(v, &vArray);CHKERRQ(ierr);
5285   }
5286   if (csize) *csize = asize;
5287   /* Cleanup points */
5288   ierr = DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp);CHKERRQ(ierr);
5289   PetscFunctionReturn(0);
5290 }
5291 
5292 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
5293 {
5294   DMLabel            depthLabel;
5295   PetscSection       clSection;
5296   IS                 clPoints;
5297   PetscScalar       *array;
5298   const PetscScalar *vArray;
5299   PetscInt          *points = NULL;
5300   const PetscInt    *clp, *perm = NULL;
5301   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
5302   PetscErrorCode     ierr;
5303 
5304   PetscFunctionBeginHot;
5305   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5306   if (!section) {ierr = DMGetLocalSection(dm, &section);CHKERRQ(ierr);}
5307   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5308   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5309   ierr = DMPlexGetDepth(dm, &mdepth);CHKERRQ(ierr);
5310   ierr = DMPlexGetDepthLabel(dm, &depthLabel);CHKERRQ(ierr);
5311   ierr = PetscSectionGetNumFields(section, &numFields);CHKERRQ(ierr);
5312   if (mdepth == 1 && numFields < 2) {
5313     ierr = DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values);CHKERRQ(ierr);
5314     PetscFunctionReturn(0);
5315   }
5316   /* Get points */
5317   ierr = DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp);CHKERRQ(ierr);
5318   for (clsize=0,p=0; p<Np; p++) {
5319     PetscInt dof;
5320     ierr = PetscSectionGetDof(section, points[2*p], &dof);CHKERRQ(ierr);
5321     clsize += dof;
5322   }
5323   ierr = PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, clsize, &perm);CHKERRQ(ierr);
5324   /* Filter points */
5325   for (p = 0; p < numPoints*2; p += 2) {
5326     PetscInt dep;
5327 
5328     ierr = DMLabelGetValue(depthLabel, points[p], &dep);CHKERRQ(ierr);
5329     if (dep != depth) continue;
5330     points[Np*2+0] = points[p];
5331     points[Np*2+1] = points[p+1];
5332     ++Np;
5333   }
5334   /* Get array */
5335   if (!values || !*values) {
5336     PetscInt asize = 0, dof;
5337 
5338     for (p = 0; p < Np*2; p += 2) {
5339       ierr = PetscSectionGetDof(section, points[p], &dof);CHKERRQ(ierr);
5340       asize += dof;
5341     }
5342     if (!values) {
5343       ierr = DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp);CHKERRQ(ierr);
5344       if (csize) *csize = asize;
5345       PetscFunctionReturn(0);
5346     }
5347     ierr = DMGetWorkArray(dm, asize, MPIU_SCALAR, &array);CHKERRQ(ierr);
5348   } else {
5349     array = *values;
5350   }
5351   ierr = VecGetArrayRead(v, &vArray);CHKERRQ(ierr);
5352   /* Get values */
5353   if (numFields > 0) {ierr = DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array);CHKERRQ(ierr);}
5354   else               {ierr = DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array);CHKERRQ(ierr);}
5355   /* Cleanup points */
5356   ierr = DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp);CHKERRQ(ierr);
5357   /* Cleanup array */
5358   ierr = VecRestoreArrayRead(v, &vArray);CHKERRQ(ierr);
5359   if (!*values) {
5360     if (csize) *csize = size;
5361     *values = array;
5362   } else {
5363     if (size > *csize) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %D < actual size %D", *csize, size);
5364     *csize = size;
5365   }
5366   PetscFunctionReturn(0);
5367 }
5368 
5369 /*@C
5370   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
5371 
5372   Not collective
5373 
5374   Input Parameters:
5375 + dm - The DM
5376 . section - The section describing the layout in v, or NULL to use the default section
5377 . v - The local vector
5378 . point - The point in the DM
5379 . csize - The number of values in the closure, or NULL
5380 - values - The array of values, which is a borrowed array and should not be freed
5381 
5382   Note that the array values are discarded and not copied back into v. In order to copy values back to v, use DMPlexVecSetClosure()
5383 
5384   Fortran Notes:
5385   Since it returns an array, this routine is only available in Fortran 90, and you must
5386   include petsc.h90 in your code.
5387 
5388   The csize argument is not present in the Fortran 90 binding since it is internal to the array.
5389 
5390   Level: intermediate
5391 
5392 .seealso DMPlexVecGetClosure(), DMPlexVecSetClosure(), DMPlexMatSetClosure()
5393 @*/
5394 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5395 {
5396   PetscInt       size = 0;
5397   PetscErrorCode ierr;
5398 
5399   PetscFunctionBegin;
5400   /* Should work without recalculating size */
5401   ierr = DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void*) values);CHKERRQ(ierr);
5402   *values = NULL;
5403   PetscFunctionReturn(0);
5404 }
5405 
5406 PETSC_STATIC_INLINE void add   (PetscScalar *x, PetscScalar y) {*x += y;}
5407 PETSC_STATIC_INLINE void insert(PetscScalar *x, PetscScalar y) {*x  = y;}
5408 
5409 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[])
5410 {
5411   PetscInt        cdof;   /* The number of constraints on this point */
5412   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
5413   PetscScalar    *a;
5414   PetscInt        off, cind = 0, k;
5415   PetscErrorCode  ierr;
5416 
5417   PetscFunctionBegin;
5418   ierr = PetscSectionGetConstraintDof(section, point, &cdof);CHKERRQ(ierr);
5419   ierr = PetscSectionGetOffset(section, point, &off);CHKERRQ(ierr);
5420   a    = &array[off];
5421   if (!cdof || setBC) {
5422     if (clperm) {
5423       if (perm) {for (k = 0; k < dof; ++k) {fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));}}
5424       else      {for (k = 0; k < dof; ++k) {fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));}}
5425     } else {
5426       if (perm) {for (k = 0; k < dof; ++k) {fuse(&a[k], values[offset+perm[k]] * (flip ? flip[perm[k]] : 1.));}}
5427       else      {for (k = 0; k < dof; ++k) {fuse(&a[k], values[offset+     k ] * (flip ? flip[     k ] : 1.));}}
5428     }
5429   } else {
5430     ierr = PetscSectionGetConstraintIndices(section, point, &cdofs);CHKERRQ(ierr);
5431     if (clperm) {
5432       if (perm) {for (k = 0; k < dof; ++k) {
5433           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5434           fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));
5435         }
5436       } else {
5437         for (k = 0; k < dof; ++k) {
5438           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5439           fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));
5440         }
5441       }
5442     } else {
5443       if (perm) {
5444         for (k = 0; k < dof; ++k) {
5445           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5446           fuse(&a[k], values[offset+perm[k]] * (flip ? flip[perm[k]] : 1.));
5447         }
5448       } else {
5449         for (k = 0; k < dof; ++k) {
5450           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5451           fuse(&a[k], values[offset+     k ] * (flip ? flip[     k ] : 1.));
5452         }
5453       }
5454     }
5455   }
5456   PetscFunctionReturn(0);
5457 }
5458 
5459 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[])
5460 {
5461   PetscInt        cdof;   /* The number of constraints on this point */
5462   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
5463   PetscScalar    *a;
5464   PetscInt        off, cind = 0, k;
5465   PetscErrorCode  ierr;
5466 
5467   PetscFunctionBegin;
5468   ierr = PetscSectionGetConstraintDof(section, point, &cdof);CHKERRQ(ierr);
5469   ierr = PetscSectionGetOffset(section, point, &off);CHKERRQ(ierr);
5470   a    = &array[off];
5471   if (cdof) {
5472     ierr = PetscSectionGetConstraintIndices(section, point, &cdofs);CHKERRQ(ierr);
5473     if (clperm) {
5474       if (perm) {
5475         for (k = 0; k < dof; ++k) {
5476           if ((cind < cdof) && (k == cdofs[cind])) {
5477             fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));
5478             cind++;
5479           }
5480         }
5481       } else {
5482         for (k = 0; k < dof; ++k) {
5483           if ((cind < cdof) && (k == cdofs[cind])) {
5484             fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));
5485             cind++;
5486           }
5487         }
5488       }
5489     } else {
5490       if (perm) {
5491         for (k = 0; k < dof; ++k) {
5492           if ((cind < cdof) && (k == cdofs[cind])) {
5493             fuse(&a[k], values[offset+perm[k]] * (flip ? flip[perm[k]] : 1.));
5494             cind++;
5495           }
5496         }
5497       } else {
5498         for (k = 0; k < dof; ++k) {
5499           if ((cind < cdof) && (k == cdofs[cind])) {
5500             fuse(&a[k], values[offset+     k ] * (flip ? flip[     k ] : 1.));
5501             cind++;
5502           }
5503         }
5504       }
5505     }
5506   }
5507   PetscFunctionReturn(0);
5508 }
5509 
5510 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[])
5511 {
5512   PetscScalar    *a;
5513   PetscInt        fdof, foff, fcdof, foffset = *offset;
5514   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
5515   PetscInt        cind = 0, b;
5516   PetscErrorCode  ierr;
5517 
5518   PetscFunctionBegin;
5519   ierr = PetscSectionGetFieldDof(section, point, f, &fdof);CHKERRQ(ierr);
5520   ierr = PetscSectionGetFieldConstraintDof(section, point, f, &fcdof);CHKERRQ(ierr);
5521   ierr = PetscSectionGetFieldOffset(section, point, f, &foff);CHKERRQ(ierr);
5522   a    = &array[foff];
5523   if (!fcdof || setBC) {
5524     if (clperm) {
5525       if (perm) {for (b = 0; b < fdof; b++) {fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));}}
5526       else      {for (b = 0; b < fdof; b++) {fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));}}
5527     } else {
5528       if (perm) {for (b = 0; b < fdof; b++) {fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));}}
5529       else      {for (b = 0; b < fdof; b++) {fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));}}
5530     }
5531   } else {
5532     ierr = PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs);CHKERRQ(ierr);
5533     if (clperm) {
5534       if (perm) {
5535         for (b = 0; b < fdof; b++) {
5536           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
5537           fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));
5538         }
5539       } else {
5540         for (b = 0; b < fdof; b++) {
5541           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
5542           fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));
5543         }
5544       }
5545     } else {
5546       if (perm) {
5547         for (b = 0; b < fdof; b++) {
5548           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
5549           fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));
5550         }
5551       } else {
5552         for (b = 0; b < fdof; b++) {
5553           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
5554           fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));
5555         }
5556       }
5557     }
5558   }
5559   *offset += fdof;
5560   PetscFunctionReturn(0);
5561 }
5562 
5563 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[])
5564 {
5565   PetscScalar    *a;
5566   PetscInt        fdof, foff, fcdof, foffset = *offset;
5567   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
5568   PetscInt        Nc, cind = 0, ncind = 0, b;
5569   PetscBool       ncSet, fcSet;
5570   PetscErrorCode  ierr;
5571 
5572   PetscFunctionBegin;
5573   ierr = PetscSectionGetFieldComponents(section, f, &Nc);CHKERRQ(ierr);
5574   ierr = PetscSectionGetFieldDof(section, point, f, &fdof);CHKERRQ(ierr);
5575   ierr = PetscSectionGetFieldConstraintDof(section, point, f, &fcdof);CHKERRQ(ierr);
5576   ierr = PetscSectionGetFieldOffset(section, point, f, &foff);CHKERRQ(ierr);
5577   a    = &array[foff];
5578   if (fcdof) {
5579     /* We just override fcdof and fcdofs with Ncc and comps */
5580     ierr = PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs);CHKERRQ(ierr);
5581     if (clperm) {
5582       if (perm) {
5583         if (comps) {
5584           for (b = 0; b < fdof; b++) {
5585             ncSet = fcSet = PETSC_FALSE;
5586             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
5587             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
5588             if (ncSet && fcSet) {fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));}
5589           }
5590         } else {
5591           for (b = 0; b < fdof; b++) {
5592             if ((cind < fcdof) && (b == fcdofs[cind])) {
5593               fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));
5594               ++cind;
5595             }
5596           }
5597         }
5598       } else {
5599         if (comps) {
5600           for (b = 0; b < fdof; b++) {
5601             ncSet = fcSet = PETSC_FALSE;
5602             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
5603             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
5604             if (ncSet && fcSet) {fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));}
5605           }
5606         } else {
5607           for (b = 0; b < fdof; b++) {
5608             if ((cind < fcdof) && (b == fcdofs[cind])) {
5609               fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));
5610               ++cind;
5611             }
5612           }
5613         }
5614       }
5615     } else {
5616       if (perm) {
5617         if (comps) {
5618           for (b = 0; b < fdof; b++) {
5619             ncSet = fcSet = PETSC_FALSE;
5620             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
5621             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
5622             if (ncSet && fcSet) {fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));}
5623           }
5624         } else {
5625           for (b = 0; b < fdof; b++) {
5626             if ((cind < fcdof) && (b == fcdofs[cind])) {
5627               fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));
5628               ++cind;
5629             }
5630           }
5631         }
5632       } else {
5633         if (comps) {
5634           for (b = 0; b < fdof; b++) {
5635             ncSet = fcSet = PETSC_FALSE;
5636             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
5637             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
5638             if (ncSet && fcSet) {fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));}
5639           }
5640         } else {
5641           for (b = 0; b < fdof; b++) {
5642             if ((cind < fcdof) && (b == fcdofs[cind])) {
5643               fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));
5644               ++cind;
5645             }
5646           }
5647         }
5648       }
5649     }
5650   }
5651   *offset += fdof;
5652   PetscFunctionReturn(0);
5653 }
5654 
5655 PETSC_STATIC_INLINE PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
5656 {
5657   PetscScalar    *array;
5658   const PetscInt *cone, *coneO;
5659   PetscInt        pStart, pEnd, p, numPoints, off, dof;
5660   PetscErrorCode  ierr;
5661 
5662   PetscFunctionBeginHot;
5663   ierr = PetscSectionGetChart(section, &pStart, &pEnd);CHKERRQ(ierr);
5664   ierr = DMPlexGetConeSize(dm, point, &numPoints);CHKERRQ(ierr);
5665   ierr = DMPlexGetCone(dm, point, &cone);CHKERRQ(ierr);
5666   ierr = DMPlexGetConeOrientation(dm, point, &coneO);CHKERRQ(ierr);
5667   ierr = VecGetArray(v, &array);CHKERRQ(ierr);
5668   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
5669     const PetscInt cp = !p ? point : cone[p-1];
5670     const PetscInt o  = !p ? 0     : coneO[p-1];
5671 
5672     if ((cp < pStart) || (cp >= pEnd)) {dof = 0; continue;}
5673     ierr = PetscSectionGetDof(section, cp, &dof);CHKERRQ(ierr);
5674     /* ADD_VALUES */
5675     {
5676       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
5677       PetscScalar    *a;
5678       PetscInt        cdof, coff, cind = 0, k;
5679 
5680       ierr = PetscSectionGetConstraintDof(section, cp, &cdof);CHKERRQ(ierr);
5681       ierr = PetscSectionGetOffset(section, cp, &coff);CHKERRQ(ierr);
5682       a    = &array[coff];
5683       if (!cdof) {
5684         if (o >= 0) {
5685           for (k = 0; k < dof; ++k) {
5686             a[k] += values[off+k];
5687           }
5688         } else {
5689           for (k = 0; k < dof; ++k) {
5690             a[k] += values[off+dof-k-1];
5691           }
5692         }
5693       } else {
5694         ierr = PetscSectionGetConstraintIndices(section, cp, &cdofs);CHKERRQ(ierr);
5695         if (o >= 0) {
5696           for (k = 0; k < dof; ++k) {
5697             if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5698             a[k] += values[off+k];
5699           }
5700         } else {
5701           for (k = 0; k < dof; ++k) {
5702             if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5703             a[k] += values[off+dof-k-1];
5704           }
5705         }
5706       }
5707     }
5708   }
5709   ierr = VecRestoreArray(v, &array);CHKERRQ(ierr);
5710   PetscFunctionReturn(0);
5711 }
5712 
5713 /*@C
5714   DMPlexVecSetClosure - Set an array of the values on the closure of 'point'
5715 
5716   Not collective
5717 
5718   Input Parameters:
5719 + dm - The DM
5720 . section - The section describing the layout in v, or NULL to use the default section
5721 . v - The local vector
5722 . point - The point in the DM
5723 . values - The array of values
5724 - mode - The insert mode. One of INSERT_ALL_VALUES, ADD_ALL_VALUES, INSERT_VALUES, ADD_VALUES, INSERT_BC_VALUES, and ADD_BC_VALUES,
5725          where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions.
5726 
5727   Fortran Notes:
5728   This routine is only available in Fortran 90, and you must include petsc.h90 in your code.
5729 
5730   Level: intermediate
5731 
5732 .seealso DMPlexVecGetClosure(), DMPlexMatSetClosure()
5733 @*/
5734 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
5735 {
5736   PetscSection    clSection;
5737   IS              clPoints;
5738   PetscScalar    *array;
5739   PetscInt       *points = NULL;
5740   const PetscInt *clp, *clperm = NULL;
5741   PetscInt        depth, numFields, numPoints, p, clsize;
5742   PetscErrorCode  ierr;
5743 
5744   PetscFunctionBeginHot;
5745   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5746   if (!section) {ierr = DMGetLocalSection(dm, &section);CHKERRQ(ierr);}
5747   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5748   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5749   ierr = DMPlexGetDepth(dm, &depth);CHKERRQ(ierr);
5750   ierr = PetscSectionGetNumFields(section, &numFields);CHKERRQ(ierr);
5751   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
5752     ierr = DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode);CHKERRQ(ierr);
5753     PetscFunctionReturn(0);
5754   }
5755   /* Get points */
5756   ierr = DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp);CHKERRQ(ierr);
5757   for (clsize=0,p=0; p<numPoints; p++) {
5758     PetscInt dof;
5759     ierr = PetscSectionGetDof(section, points[2*p], &dof);CHKERRQ(ierr);
5760     clsize += dof;
5761   }
5762   ierr = PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, clsize, &clperm);CHKERRQ(ierr);
5763   /* Get array */
5764   ierr = VecGetArray(v, &array);CHKERRQ(ierr);
5765   /* Get values */
5766   if (numFields > 0) {
5767     PetscInt offset = 0, f;
5768     for (f = 0; f < numFields; ++f) {
5769       const PetscInt    **perms = NULL;
5770       const PetscScalar **flips = NULL;
5771 
5772       ierr = PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips);CHKERRQ(ierr);
5773       switch (mode) {
5774       case INSERT_VALUES:
5775         for (p = 0; p < numPoints; p++) {
5776           const PetscInt    point = points[2*p];
5777           const PetscInt    *perm = perms ? perms[p] : NULL;
5778           const PetscScalar *flip = flips ? flips[p] : NULL;
5779           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array);
5780         } break;
5781       case INSERT_ALL_VALUES:
5782         for (p = 0; p < numPoints; p++) {
5783           const PetscInt    point = points[2*p];
5784           const PetscInt    *perm = perms ? perms[p] : NULL;
5785           const PetscScalar *flip = flips ? flips[p] : NULL;
5786           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array);
5787         } break;
5788       case INSERT_BC_VALUES:
5789         for (p = 0; p < numPoints; p++) {
5790           const PetscInt    point = points[2*p];
5791           const PetscInt    *perm = perms ? perms[p] : NULL;
5792           const PetscScalar *flip = flips ? flips[p] : NULL;
5793           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array);
5794         } break;
5795       case ADD_VALUES:
5796         for (p = 0; p < numPoints; p++) {
5797           const PetscInt    point = points[2*p];
5798           const PetscInt    *perm = perms ? perms[p] : NULL;
5799           const PetscScalar *flip = flips ? flips[p] : NULL;
5800           updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array);
5801         } break;
5802       case ADD_ALL_VALUES:
5803         for (p = 0; p < numPoints; p++) {
5804           const PetscInt    point = points[2*p];
5805           const PetscInt    *perm = perms ? perms[p] : NULL;
5806           const PetscScalar *flip = flips ? flips[p] : NULL;
5807           updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array);
5808         } break;
5809       case ADD_BC_VALUES:
5810         for (p = 0; p < numPoints; p++) {
5811           const PetscInt    point = points[2*p];
5812           const PetscInt    *perm = perms ? perms[p] : NULL;
5813           const PetscScalar *flip = flips ? flips[p] : NULL;
5814           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array);
5815         } break;
5816       default:
5817         SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
5818       }
5819       ierr = PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips);CHKERRQ(ierr);
5820     }
5821   } else {
5822     PetscInt dof, off;
5823     const PetscInt    **perms = NULL;
5824     const PetscScalar **flips = NULL;
5825 
5826     ierr = PetscSectionGetPointSyms(section,numPoints,points,&perms,&flips);CHKERRQ(ierr);
5827     switch (mode) {
5828     case INSERT_VALUES:
5829       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
5830         const PetscInt    point = points[2*p];
5831         const PetscInt    *perm = perms ? perms[p] : NULL;
5832         const PetscScalar *flip = flips ? flips[p] : NULL;
5833         ierr = PetscSectionGetDof(section, point, &dof);CHKERRQ(ierr);
5834         updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array);
5835       } break;
5836     case INSERT_ALL_VALUES:
5837       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
5838         const PetscInt    point = points[2*p];
5839         const PetscInt    *perm = perms ? perms[p] : NULL;
5840         const PetscScalar *flip = flips ? flips[p] : NULL;
5841         ierr = PetscSectionGetDof(section, point, &dof);CHKERRQ(ierr);
5842         updatePoint_private(section, point, dof, insert, PETSC_TRUE,  perm, flip, clperm, values, off, array);
5843       } break;
5844     case INSERT_BC_VALUES:
5845       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
5846         const PetscInt    point = points[2*p];
5847         const PetscInt    *perm = perms ? perms[p] : NULL;
5848         const PetscScalar *flip = flips ? flips[p] : NULL;
5849         ierr = PetscSectionGetDof(section, point, &dof);CHKERRQ(ierr);
5850         updatePointBC_private(section, point, dof, insert,  perm, flip, clperm, values, off, array);
5851       } break;
5852     case ADD_VALUES:
5853       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
5854         const PetscInt    point = points[2*p];
5855         const PetscInt    *perm = perms ? perms[p] : NULL;
5856         const PetscScalar *flip = flips ? flips[p] : NULL;
5857         ierr = PetscSectionGetDof(section, point, &dof);CHKERRQ(ierr);
5858         updatePoint_private(section, point, dof, add,    PETSC_FALSE, perm, flip, clperm, values, off, array);
5859       } break;
5860     case ADD_ALL_VALUES:
5861       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
5862         const PetscInt    point = points[2*p];
5863         const PetscInt    *perm = perms ? perms[p] : NULL;
5864         const PetscScalar *flip = flips ? flips[p] : NULL;
5865         ierr = PetscSectionGetDof(section, point, &dof);CHKERRQ(ierr);
5866         updatePoint_private(section, point, dof, add,    PETSC_TRUE,  perm, flip, clperm, values, off, array);
5867       } break;
5868     case ADD_BC_VALUES:
5869       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
5870         const PetscInt    point = points[2*p];
5871         const PetscInt    *perm = perms ? perms[p] : NULL;
5872         const PetscScalar *flip = flips ? flips[p] : NULL;
5873         ierr = PetscSectionGetDof(section, point, &dof);CHKERRQ(ierr);
5874         updatePointBC_private(section, point, dof, add,  perm, flip, clperm, values, off, array);
5875       } break;
5876     default:
5877       SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
5878     }
5879     ierr = PetscSectionRestorePointSyms(section,numPoints,points,&perms,&flips);CHKERRQ(ierr);
5880   }
5881   /* Cleanup points */
5882   ierr = DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp);CHKERRQ(ierr);
5883   /* Cleanup array */
5884   ierr = VecRestoreArray(v, &array);CHKERRQ(ierr);
5885   PetscFunctionReturn(0);
5886 }
5887 
5888 /* Check whether the given point is in the label. If not, update the offset to skip this point */
5889 PETSC_STATIC_INLINE PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset)
5890 {
5891   PetscFunctionBegin;
5892   if (label) {
5893     PetscInt       val, fdof;
5894     PetscErrorCode ierr;
5895 
5896     /* There is a problem with this:
5897          Suppose we have two label values, defining surfaces, interecting along a line in 3D. When we add cells to the label, the cells that
5898        touch both surfaces must pick a label value. Thus we miss setting values for the surface with that other value intersecting that cell.
5899        Thus I am only going to check val != -1, not val != labelId
5900     */
5901     ierr = DMLabelGetValue(label, point, &val);CHKERRQ(ierr);
5902     if (val < 0) {
5903       ierr = PetscSectionGetFieldDof(section, point, f, &fdof);CHKERRQ(ierr);
5904       *offset += fdof;
5905       PetscFunctionReturn(1);
5906     }
5907   }
5908   PetscFunctionReturn(0);
5909 }
5910 
5911 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
5912 PetscErrorCode DMPlexVecSetFieldClosure_Internal(DM dm, PetscSection section, Vec v, PetscBool fieldActive[], PetscInt point, PetscInt Ncc, const PetscInt comps[], DMLabel label, PetscInt labelId, const PetscScalar values[], InsertMode mode)
5913 {
5914   PetscSection      clSection;
5915   IS                clPoints;
5916   PetscScalar       *array;
5917   PetscInt          *points = NULL;
5918   const PetscInt    *clp;
5919   PetscInt          numFields, numPoints, p;
5920   PetscInt          offset = 0, f;
5921   PetscErrorCode    ierr;
5922 
5923   PetscFunctionBeginHot;
5924   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5925   if (!section) {ierr = DMGetLocalSection(dm, &section);CHKERRQ(ierr);}
5926   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5927   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5928   ierr = PetscSectionGetNumFields(section, &numFields);CHKERRQ(ierr);
5929   /* Get points */
5930   ierr = DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp);CHKERRQ(ierr);
5931   /* Get array */
5932   ierr = VecGetArray(v, &array);CHKERRQ(ierr);
5933   /* Get values */
5934   for (f = 0; f < numFields; ++f) {
5935     const PetscInt    **perms = NULL;
5936     const PetscScalar **flips = NULL;
5937 
5938     if (!fieldActive[f]) {
5939       for (p = 0; p < numPoints*2; p += 2) {
5940         PetscInt fdof;
5941         ierr = PetscSectionGetFieldDof(section, points[p], f, &fdof);CHKERRQ(ierr);
5942         offset += fdof;
5943       }
5944       continue;
5945     }
5946     ierr = PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips);CHKERRQ(ierr);
5947     switch (mode) {
5948     case INSERT_VALUES:
5949       for (p = 0; p < numPoints; p++) {
5950         const PetscInt    point = points[2*p];
5951         const PetscInt    *perm = perms ? perms[p] : NULL;
5952         const PetscScalar *flip = flips ? flips[p] : NULL;
5953         ierr = CheckPoint_Private(label, labelId, section, point, f, &offset); if (ierr) continue;
5954         updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array);
5955       } break;
5956     case INSERT_ALL_VALUES:
5957       for (p = 0; p < numPoints; p++) {
5958         const PetscInt    point = points[2*p];
5959         const PetscInt    *perm = perms ? perms[p] : NULL;
5960         const PetscScalar *flip = flips ? flips[p] : NULL;
5961         ierr = CheckPoint_Private(label, labelId, section, point, f, &offset); if (ierr) continue;
5962         updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array);
5963       } break;
5964     case INSERT_BC_VALUES:
5965       for (p = 0; p < numPoints; p++) {
5966         const PetscInt    point = points[2*p];
5967         const PetscInt    *perm = perms ? perms[p] : NULL;
5968         const PetscScalar *flip = flips ? flips[p] : NULL;
5969         ierr = CheckPoint_Private(label, labelId, section, point, f, &offset); if (ierr) continue;
5970         updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array);
5971       } break;
5972     case ADD_VALUES:
5973       for (p = 0; p < numPoints; p++) {
5974         const PetscInt    point = points[2*p];
5975         const PetscInt    *perm = perms ? perms[p] : NULL;
5976         const PetscScalar *flip = flips ? flips[p] : NULL;
5977         ierr = CheckPoint_Private(label, labelId, section, point, f, &offset); if (ierr) continue;
5978         updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array);
5979       } break;
5980     case ADD_ALL_VALUES:
5981       for (p = 0; p < numPoints; p++) {
5982         const PetscInt    point = points[2*p];
5983         const PetscInt    *perm = perms ? perms[p] : NULL;
5984         const PetscScalar *flip = flips ? flips[p] : NULL;
5985         ierr = CheckPoint_Private(label, labelId, section, point, f, &offset); if (ierr) continue;
5986         updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array);
5987       } break;
5988     default:
5989       SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
5990     }
5991     ierr = PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips);CHKERRQ(ierr);
5992   }
5993   /* Cleanup points */
5994   ierr = DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp);CHKERRQ(ierr);
5995   /* Cleanup array */
5996   ierr = VecRestoreArray(v, &array);CHKERRQ(ierr);
5997   PetscFunctionReturn(0);
5998 }
5999 
6000 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
6001 {
6002   PetscMPIInt    rank;
6003   PetscInt       i, j;
6004   PetscErrorCode ierr;
6005 
6006   PetscFunctionBegin;
6007   ierr = MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank);CHKERRMPI(ierr);
6008   ierr = PetscViewerASCIIPrintf(viewer, "[%d]mat for point %D\n", rank, point);CHKERRQ(ierr);
6009   for (i = 0; i < numRIndices; i++) {ierr = PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%D] = %D\n", rank, i, rindices[i]);CHKERRQ(ierr);}
6010   for (i = 0; i < numCIndices; i++) {ierr = PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%D] = %D\n", rank, i, cindices[i]);CHKERRQ(ierr);}
6011   numCIndices = numCIndices ? numCIndices : numRIndices;
6012   if (!values) PetscFunctionReturn(0);
6013   for (i = 0; i < numRIndices; i++) {
6014     ierr = PetscViewerASCIIPrintf(viewer, "[%d]", rank);CHKERRQ(ierr);
6015     for (j = 0; j < numCIndices; j++) {
6016 #if defined(PETSC_USE_COMPLEX)
6017       ierr = PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i*numCIndices+j]), (double)PetscImaginaryPart(values[i*numCIndices+j]));CHKERRQ(ierr);
6018 #else
6019       ierr = PetscViewerASCIIPrintf(viewer, " %g", (double)values[i*numCIndices+j]);CHKERRQ(ierr);
6020 #endif
6021     }
6022     ierr = PetscViewerASCIIPrintf(viewer, "\n");CHKERRQ(ierr);
6023   }
6024   PetscFunctionReturn(0);
6025 }
6026 
6027 /*
6028   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
6029 
6030   Input Parameters:
6031 + section - The section for this data layout
6032 . islocal - Is the section (and thus indices being requested) local or global?
6033 . point   - The point contributing dofs with these indices
6034 . off     - The global offset of this point
6035 . loff    - The local offset of each field
6036 . setBC   - The flag determining whether to include indices of bounsary values
6037 . perm    - A permutation of the dofs on this point, or NULL
6038 - indperm - A permutation of the entire indices array, or NULL
6039 
6040   Output Parameter:
6041 . indices - Indices for dofs on this point
6042 
6043   Level: developer
6044 
6045   Note: The indices could be local or global, depending on the value of 'off'.
6046 */
6047 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal,PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
6048 {
6049   PetscInt        dof;   /* The number of unknowns on this point */
6050   PetscInt        cdof;  /* The number of constraints on this point */
6051   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6052   PetscInt        cind = 0, k;
6053   PetscErrorCode  ierr;
6054 
6055   PetscFunctionBegin;
6056   if (!islocal && setBC) SETERRQ(PetscObjectComm((PetscObject)section),PETSC_ERR_ARG_INCOMP,"setBC incompatible with global indices; use a local section or disable setBC");
6057   ierr = PetscSectionGetDof(section, point, &dof);CHKERRQ(ierr);
6058   ierr = PetscSectionGetConstraintDof(section, point, &cdof);CHKERRQ(ierr);
6059   if (!cdof || setBC) {
6060     for (k = 0; k < dof; ++k) {
6061       const PetscInt preind = perm ? *loff+perm[k] : *loff+k;
6062       const PetscInt ind    = indperm ? indperm[preind] : preind;
6063 
6064       indices[ind] = off + k;
6065     }
6066   } else {
6067     ierr = PetscSectionGetConstraintIndices(section, point, &cdofs);CHKERRQ(ierr);
6068     for (k = 0; k < dof; ++k) {
6069       const PetscInt preind = perm ? *loff+perm[k] : *loff+k;
6070       const PetscInt ind    = indperm ? indperm[preind] : preind;
6071 
6072       if ((cind < cdof) && (k == cdofs[cind])) {
6073         /* Insert check for returning constrained indices */
6074         indices[ind] = -(off+k+1);
6075         ++cind;
6076       } else {
6077         indices[ind] = off + k - (islocal ? 0 : cind);
6078       }
6079     }
6080   }
6081   *loff += dof;
6082   PetscFunctionReturn(0);
6083 }
6084 
6085 /*
6086  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
6087 
6088  Input Parameters:
6089 + section - a section (global or local)
6090 - islocal - PETSC_TRUE if requesting local indices (i.e., section is local); PETSC_FALSE for global
6091 . point - point within section
6092 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
6093 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
6094 . setBC - identify constrained (boundary condition) points via involution.
6095 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
6096 . permsoff - offset
6097 - indperm - index permutation
6098 
6099  Output Parameter:
6100 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
6101 . indices - array to hold indices (as defined by section) of each dof associated with point
6102 
6103  Notes:
6104  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
6105  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
6106  in the local vector.
6107 
6108  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
6109  significant).  It is invalid to call with a global section and setBC=true.
6110 
6111  Developer Note:
6112  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
6113  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
6114  offset could be obtained from the section instead of passing it explicitly as we do now.
6115 
6116  Example:
6117  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
6118  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
6119  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
6120  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.
6121 
6122  Level: developer
6123 */
6124 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[])
6125 {
6126   PetscInt       numFields, foff, f;
6127   PetscErrorCode ierr;
6128 
6129   PetscFunctionBegin;
6130   if (!islocal && setBC) SETERRQ(PetscObjectComm((PetscObject)section),PETSC_ERR_ARG_INCOMP,"setBC incompatible with global indices; use a local section or disable setBC");
6131   ierr = PetscSectionGetNumFields(section, &numFields);CHKERRQ(ierr);
6132   for (f = 0, foff = 0; f < numFields; ++f) {
6133     PetscInt        fdof, cfdof;
6134     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6135     PetscInt        cind = 0, b;
6136     const PetscInt  *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6137 
6138     ierr = PetscSectionGetFieldDof(section, point, f, &fdof);CHKERRQ(ierr);
6139     ierr = PetscSectionGetFieldConstraintDof(section, point, f, &cfdof);CHKERRQ(ierr);
6140     if (!cfdof || setBC) {
6141       for (b = 0; b < fdof; ++b) {
6142         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6143         const PetscInt ind    = indperm ? indperm[preind] : preind;
6144 
6145         indices[ind] = off+foff+b;
6146       }
6147     } else {
6148       ierr = PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs);CHKERRQ(ierr);
6149       for (b = 0; b < fdof; ++b) {
6150         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6151         const PetscInt ind    = indperm ? indperm[preind] : preind;
6152 
6153         if ((cind < cfdof) && (b == fcdofs[cind])) {
6154           indices[ind] = -(off+foff+b+1);
6155           ++cind;
6156         } else {
6157           indices[ind] = off + foff + b - (islocal ? 0 : cind);
6158         }
6159       }
6160     }
6161     foff     += (setBC || islocal ? fdof : (fdof - cfdof));
6162     foffs[f] += fdof;
6163   }
6164   PetscFunctionReturn(0);
6165 }
6166 
6167 /*
6168   This version believes the globalSection offsets for each field, rather than just the point offset
6169 
6170  . foffs - The offset into 'indices' for each field, since it is segregated by field
6171 
6172  Notes:
6173  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
6174  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
6175 */
6176 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
6177 {
6178   PetscInt       numFields, foff, f;
6179   PetscErrorCode ierr;
6180 
6181   PetscFunctionBegin;
6182   ierr = PetscSectionGetNumFields(section, &numFields);CHKERRQ(ierr);
6183   for (f = 0; f < numFields; ++f) {
6184     PetscInt        fdof, cfdof;
6185     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6186     PetscInt        cind = 0, b;
6187     const PetscInt  *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6188 
6189     ierr = PetscSectionGetFieldDof(section, point, f, &fdof);CHKERRQ(ierr);
6190     ierr = PetscSectionGetFieldConstraintDof(section, point, f, &cfdof);CHKERRQ(ierr);
6191     ierr = PetscSectionGetFieldOffset(globalSection, point, f, &foff);CHKERRQ(ierr);
6192     if (!cfdof) {
6193       for (b = 0; b < fdof; ++b) {
6194         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6195         const PetscInt ind    = indperm ? indperm[preind] : preind;
6196 
6197         indices[ind] = foff+b;
6198       }
6199     } else {
6200       ierr = PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs);CHKERRQ(ierr);
6201       for (b = 0; b < fdof; ++b) {
6202         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6203         const PetscInt ind    = indperm ? indperm[preind] : preind;
6204 
6205         if ((cind < cfdof) && (b == fcdofs[cind])) {
6206           indices[ind] = -(foff+b+1);
6207           ++cind;
6208         } else {
6209           indices[ind] = foff+b-cind;
6210         }
6211       }
6212     }
6213     foffs[f] += fdof;
6214   }
6215   PetscFunctionReturn(0);
6216 }
6217 
6218 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)
6219 {
6220   Mat             cMat;
6221   PetscSection    aSec, cSec;
6222   IS              aIS;
6223   PetscInt        aStart = -1, aEnd = -1;
6224   const PetscInt  *anchors;
6225   PetscInt        numFields, f, p, q, newP = 0;
6226   PetscInt        newNumPoints = 0, newNumIndices = 0;
6227   PetscInt        *newPoints, *indices, *newIndices;
6228   PetscInt        maxAnchor, maxDof;
6229   PetscInt        newOffsets[32];
6230   PetscInt        *pointMatOffsets[32];
6231   PetscInt        *newPointOffsets[32];
6232   PetscScalar     *pointMat[32];
6233   PetscScalar     *newValues=NULL,*tmpValues;
6234   PetscBool       anyConstrained = PETSC_FALSE;
6235   PetscErrorCode  ierr;
6236 
6237   PetscFunctionBegin;
6238   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6239   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6240   ierr = PetscSectionGetNumFields(section, &numFields);CHKERRQ(ierr);
6241 
6242   ierr = DMPlexGetAnchors(dm,&aSec,&aIS);CHKERRQ(ierr);
6243   /* if there are point-to-point constraints */
6244   if (aSec) {
6245     ierr = PetscArrayzero(newOffsets, 32);CHKERRQ(ierr);
6246     ierr = ISGetIndices(aIS,&anchors);CHKERRQ(ierr);
6247     ierr = PetscSectionGetChart(aSec,&aStart,&aEnd);CHKERRQ(ierr);
6248     /* figure out how many points are going to be in the new element matrix
6249      * (we allow double counting, because it's all just going to be summed
6250      * into the global matrix anyway) */
6251     for (p = 0; p < 2*numPoints; p+=2) {
6252       PetscInt b    = points[p];
6253       PetscInt bDof = 0, bSecDof;
6254 
6255       ierr = PetscSectionGetDof(section,b,&bSecDof);CHKERRQ(ierr);
6256       if (!bSecDof) {
6257         continue;
6258       }
6259       if (b >= aStart && b < aEnd) {
6260         ierr = PetscSectionGetDof(aSec,b,&bDof);CHKERRQ(ierr);
6261       }
6262       if (bDof) {
6263         /* this point is constrained */
6264         /* it is going to be replaced by its anchors */
6265         PetscInt bOff, q;
6266 
6267         anyConstrained = PETSC_TRUE;
6268         newNumPoints  += bDof;
6269         ierr = PetscSectionGetOffset(aSec,b,&bOff);CHKERRQ(ierr);
6270         for (q = 0; q < bDof; q++) {
6271           PetscInt a = anchors[bOff + q];
6272           PetscInt aDof;
6273 
6274           ierr           = PetscSectionGetDof(section,a,&aDof);CHKERRQ(ierr);
6275           newNumIndices += aDof;
6276           for (f = 0; f < numFields; ++f) {
6277             PetscInt fDof;
6278 
6279             ierr             = PetscSectionGetFieldDof(section, a, f, &fDof);CHKERRQ(ierr);
6280             newOffsets[f+1] += fDof;
6281           }
6282         }
6283       }
6284       else {
6285         /* this point is not constrained */
6286         newNumPoints++;
6287         newNumIndices += bSecDof;
6288         for (f = 0; f < numFields; ++f) {
6289           PetscInt fDof;
6290 
6291           ierr = PetscSectionGetFieldDof(section, b, f, &fDof);CHKERRQ(ierr);
6292           newOffsets[f+1] += fDof;
6293         }
6294       }
6295     }
6296   }
6297   if (!anyConstrained) {
6298     if (outNumPoints)  *outNumPoints  = 0;
6299     if (outNumIndices) *outNumIndices = 0;
6300     if (outPoints)     *outPoints     = NULL;
6301     if (outValues)     *outValues     = NULL;
6302     if (aSec) {ierr = ISRestoreIndices(aIS,&anchors);CHKERRQ(ierr);}
6303     PetscFunctionReturn(0);
6304   }
6305 
6306   if (outNumPoints)  *outNumPoints  = newNumPoints;
6307   if (outNumIndices) *outNumIndices = newNumIndices;
6308 
6309   for (f = 0; f < numFields; ++f) newOffsets[f+1] += newOffsets[f];
6310 
6311   if (!outPoints && !outValues) {
6312     if (offsets) {
6313       for (f = 0; f <= numFields; f++) {
6314         offsets[f] = newOffsets[f];
6315       }
6316     }
6317     if (aSec) {ierr = ISRestoreIndices(aIS,&anchors);CHKERRQ(ierr);}
6318     PetscFunctionReturn(0);
6319   }
6320 
6321   if (numFields && newOffsets[numFields] != newNumIndices) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %D should be %D", newOffsets[numFields], newNumIndices);
6322 
6323   ierr = DMGetDefaultConstraints(dm, &cSec, &cMat);CHKERRQ(ierr);
6324 
6325   /* workspaces */
6326   if (numFields) {
6327     for (f = 0; f < numFields; f++) {
6328       ierr = DMGetWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[f]);CHKERRQ(ierr);
6329       ierr = DMGetWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[f]);CHKERRQ(ierr);
6330     }
6331   }
6332   else {
6333     ierr = DMGetWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[0]);CHKERRQ(ierr);
6334     ierr = DMGetWorkArray(dm,numPoints,MPIU_INT,&newPointOffsets[0]);CHKERRQ(ierr);
6335   }
6336 
6337   /* get workspaces for the point-to-point matrices */
6338   if (numFields) {
6339     PetscInt totalOffset, totalMatOffset;
6340 
6341     for (p = 0; p < numPoints; p++) {
6342       PetscInt b    = points[2*p];
6343       PetscInt bDof = 0, bSecDof;
6344 
6345       ierr = PetscSectionGetDof(section,b,&bSecDof);CHKERRQ(ierr);
6346       if (!bSecDof) {
6347         for (f = 0; f < numFields; f++) {
6348           newPointOffsets[f][p + 1] = 0;
6349           pointMatOffsets[f][p + 1] = 0;
6350         }
6351         continue;
6352       }
6353       if (b >= aStart && b < aEnd) {
6354         ierr = PetscSectionGetDof(aSec, b, &bDof);CHKERRQ(ierr);
6355       }
6356       if (bDof) {
6357         for (f = 0; f < numFields; f++) {
6358           PetscInt fDof, q, bOff, allFDof = 0;
6359 
6360           ierr = PetscSectionGetFieldDof(section, b, f, &fDof);CHKERRQ(ierr);
6361           ierr = PetscSectionGetOffset(aSec, b, &bOff);CHKERRQ(ierr);
6362           for (q = 0; q < bDof; q++) {
6363             PetscInt a = anchors[bOff + q];
6364             PetscInt aFDof;
6365 
6366             ierr     = PetscSectionGetFieldDof(section, a, f, &aFDof);CHKERRQ(ierr);
6367             allFDof += aFDof;
6368           }
6369           newPointOffsets[f][p+1] = allFDof;
6370           pointMatOffsets[f][p+1] = fDof * allFDof;
6371         }
6372       }
6373       else {
6374         for (f = 0; f < numFields; f++) {
6375           PetscInt fDof;
6376 
6377           ierr = PetscSectionGetFieldDof(section, b, f, &fDof);CHKERRQ(ierr);
6378           newPointOffsets[f][p+1] = fDof;
6379           pointMatOffsets[f][p+1] = 0;
6380         }
6381       }
6382     }
6383     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
6384       newPointOffsets[f][0] = totalOffset;
6385       pointMatOffsets[f][0] = totalMatOffset;
6386       for (p = 0; p < numPoints; p++) {
6387         newPointOffsets[f][p+1] += newPointOffsets[f][p];
6388         pointMatOffsets[f][p+1] += pointMatOffsets[f][p];
6389       }
6390       totalOffset    = newPointOffsets[f][numPoints];
6391       totalMatOffset = pointMatOffsets[f][numPoints];
6392       ierr = DMGetWorkArray(dm,pointMatOffsets[f][numPoints],MPIU_SCALAR,&pointMat[f]);CHKERRQ(ierr);
6393     }
6394   }
6395   else {
6396     for (p = 0; p < numPoints; p++) {
6397       PetscInt b    = points[2*p];
6398       PetscInt bDof = 0, bSecDof;
6399 
6400       ierr = PetscSectionGetDof(section,b,&bSecDof);CHKERRQ(ierr);
6401       if (!bSecDof) {
6402         newPointOffsets[0][p + 1] = 0;
6403         pointMatOffsets[0][p + 1] = 0;
6404         continue;
6405       }
6406       if (b >= aStart && b < aEnd) {
6407         ierr = PetscSectionGetDof(aSec, b, &bDof);CHKERRQ(ierr);
6408       }
6409       if (bDof) {
6410         PetscInt bOff, q, allDof = 0;
6411 
6412         ierr = PetscSectionGetOffset(aSec, b, &bOff);CHKERRQ(ierr);
6413         for (q = 0; q < bDof; q++) {
6414           PetscInt a = anchors[bOff + q], aDof;
6415 
6416           ierr    = PetscSectionGetDof(section, a, &aDof);CHKERRQ(ierr);
6417           allDof += aDof;
6418         }
6419         newPointOffsets[0][p+1] = allDof;
6420         pointMatOffsets[0][p+1] = bSecDof * allDof;
6421       }
6422       else {
6423         newPointOffsets[0][p+1] = bSecDof;
6424         pointMatOffsets[0][p+1] = 0;
6425       }
6426     }
6427     newPointOffsets[0][0] = 0;
6428     pointMatOffsets[0][0] = 0;
6429     for (p = 0; p < numPoints; p++) {
6430       newPointOffsets[0][p+1] += newPointOffsets[0][p];
6431       pointMatOffsets[0][p+1] += pointMatOffsets[0][p];
6432     }
6433     ierr = DMGetWorkArray(dm,pointMatOffsets[0][numPoints],MPIU_SCALAR,&pointMat[0]);CHKERRQ(ierr);
6434   }
6435 
6436   /* output arrays */
6437   ierr = DMGetWorkArray(dm,2*newNumPoints,MPIU_INT,&newPoints);CHKERRQ(ierr);
6438 
6439   /* get the point-to-point matrices; construct newPoints */
6440   ierr = PetscSectionGetMaxDof(aSec, &maxAnchor);CHKERRQ(ierr);
6441   ierr = PetscSectionGetMaxDof(section, &maxDof);CHKERRQ(ierr);
6442   ierr = DMGetWorkArray(dm,maxDof,MPIU_INT,&indices);CHKERRQ(ierr);
6443   ierr = DMGetWorkArray(dm,maxAnchor*maxDof,MPIU_INT,&newIndices);CHKERRQ(ierr);
6444   if (numFields) {
6445     for (p = 0, newP = 0; p < numPoints; p++) {
6446       PetscInt b    = points[2*p];
6447       PetscInt o    = points[2*p+1];
6448       PetscInt bDof = 0, bSecDof;
6449 
6450       ierr = PetscSectionGetDof(section, b, &bSecDof);CHKERRQ(ierr);
6451       if (!bSecDof) {
6452         continue;
6453       }
6454       if (b >= aStart && b < aEnd) {
6455         ierr = PetscSectionGetDof(aSec, b, &bDof);CHKERRQ(ierr);
6456       }
6457       if (bDof) {
6458         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
6459 
6460         fStart[0] = 0;
6461         fEnd[0]   = 0;
6462         for (f = 0; f < numFields; f++) {
6463           PetscInt fDof;
6464 
6465           ierr        = PetscSectionGetFieldDof(cSec, b, f, &fDof);CHKERRQ(ierr);
6466           fStart[f+1] = fStart[f] + fDof;
6467           fEnd[f+1]   = fStart[f+1];
6468         }
6469         ierr = PetscSectionGetOffset(cSec, b, &bOff);CHKERRQ(ierr);
6470         ierr = DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices);CHKERRQ(ierr);
6471 
6472         fAnchorStart[0] = 0;
6473         fAnchorEnd[0]   = 0;
6474         for (f = 0; f < numFields; f++) {
6475           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
6476 
6477           fAnchorStart[f+1] = fAnchorStart[f] + fDof;
6478           fAnchorEnd[f+1]   = fAnchorStart[f + 1];
6479         }
6480         ierr = PetscSectionGetOffset(aSec, b, &bOff);CHKERRQ(ierr);
6481         for (q = 0; q < bDof; q++) {
6482           PetscInt a = anchors[bOff + q], aOff;
6483 
6484           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
6485           newPoints[2*(newP + q)]     = a;
6486           newPoints[2*(newP + q) + 1] = 0;
6487           ierr = PetscSectionGetOffset(section, a, &aOff);CHKERRQ(ierr);
6488           ierr = DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices);CHKERRQ(ierr);
6489         }
6490         newP += bDof;
6491 
6492         if (outValues) {
6493           /* get the point-to-point submatrix */
6494           for (f = 0; f < numFields; f++) {
6495             ierr = MatGetValues(cMat,fEnd[f]-fStart[f],indices + fStart[f],fAnchorEnd[f] - fAnchorStart[f],newIndices + fAnchorStart[f],pointMat[f] + pointMatOffsets[f][p]);CHKERRQ(ierr);
6496           }
6497         }
6498       }
6499       else {
6500         newPoints[2 * newP]     = b;
6501         newPoints[2 * newP + 1] = o;
6502         newP++;
6503       }
6504     }
6505   } else {
6506     for (p = 0; p < numPoints; p++) {
6507       PetscInt b    = points[2*p];
6508       PetscInt o    = points[2*p+1];
6509       PetscInt bDof = 0, bSecDof;
6510 
6511       ierr = PetscSectionGetDof(section, b, &bSecDof);CHKERRQ(ierr);
6512       if (!bSecDof) {
6513         continue;
6514       }
6515       if (b >= aStart && b < aEnd) {
6516         ierr = PetscSectionGetDof(aSec, b, &bDof);CHKERRQ(ierr);
6517       }
6518       if (bDof) {
6519         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
6520 
6521         ierr = PetscSectionGetOffset(cSec, b, &bOff);CHKERRQ(ierr);
6522         ierr = DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices);CHKERRQ(ierr);
6523 
6524         ierr = PetscSectionGetOffset (aSec, b, &bOff);CHKERRQ(ierr);
6525         for (q = 0; q < bDof; q++) {
6526           PetscInt a = anchors[bOff + q], aOff;
6527 
6528           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
6529 
6530           newPoints[2*(newP + q)]     = a;
6531           newPoints[2*(newP + q) + 1] = 0;
6532           ierr = PetscSectionGetOffset(section, a, &aOff);CHKERRQ(ierr);
6533           ierr = DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices);CHKERRQ(ierr);
6534         }
6535         newP += bDof;
6536 
6537         /* get the point-to-point submatrix */
6538         if (outValues) {
6539           ierr = MatGetValues(cMat,bEnd,indices,bAnchorEnd,newIndices,pointMat[0] + pointMatOffsets[0][p]);CHKERRQ(ierr);
6540         }
6541       }
6542       else {
6543         newPoints[2 * newP]     = b;
6544         newPoints[2 * newP + 1] = o;
6545         newP++;
6546       }
6547     }
6548   }
6549 
6550   if (outValues) {
6551     ierr = DMGetWorkArray(dm,newNumIndices*numIndices,MPIU_SCALAR,&tmpValues);CHKERRQ(ierr);
6552     ierr = PetscArrayzero(tmpValues,newNumIndices*numIndices);CHKERRQ(ierr);
6553     /* multiply constraints on the right */
6554     if (numFields) {
6555       for (f = 0; f < numFields; f++) {
6556         PetscInt oldOff = offsets[f];
6557 
6558         for (p = 0; p < numPoints; p++) {
6559           PetscInt cStart = newPointOffsets[f][p];
6560           PetscInt b      = points[2 * p];
6561           PetscInt c, r, k;
6562           PetscInt dof;
6563 
6564           ierr = PetscSectionGetFieldDof(section,b,f,&dof);CHKERRQ(ierr);
6565           if (!dof) {
6566             continue;
6567           }
6568           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
6569             PetscInt nCols         = newPointOffsets[f][p+1]-cStart;
6570             const PetscScalar *mat = pointMat[f] + pointMatOffsets[f][p];
6571 
6572             for (r = 0; r < numIndices; r++) {
6573               for (c = 0; c < nCols; c++) {
6574                 for (k = 0; k < dof; k++) {
6575                   tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
6576                 }
6577               }
6578             }
6579           }
6580           else {
6581             /* copy this column as is */
6582             for (r = 0; r < numIndices; r++) {
6583               for (c = 0; c < dof; c++) {
6584                 tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
6585               }
6586             }
6587           }
6588           oldOff += dof;
6589         }
6590       }
6591     }
6592     else {
6593       PetscInt oldOff = 0;
6594       for (p = 0; p < numPoints; p++) {
6595         PetscInt cStart = newPointOffsets[0][p];
6596         PetscInt b      = points[2 * p];
6597         PetscInt c, r, k;
6598         PetscInt dof;
6599 
6600         ierr = PetscSectionGetDof(section,b,&dof);CHKERRQ(ierr);
6601         if (!dof) {
6602           continue;
6603         }
6604         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
6605           PetscInt nCols         = newPointOffsets[0][p+1]-cStart;
6606           const PetscScalar *mat = pointMat[0] + pointMatOffsets[0][p];
6607 
6608           for (r = 0; r < numIndices; r++) {
6609             for (c = 0; c < nCols; c++) {
6610               for (k = 0; k < dof; k++) {
6611                 tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
6612               }
6613             }
6614           }
6615         }
6616         else {
6617           /* copy this column as is */
6618           for (r = 0; r < numIndices; r++) {
6619             for (c = 0; c < dof; c++) {
6620               tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
6621             }
6622           }
6623         }
6624         oldOff += dof;
6625       }
6626     }
6627 
6628     if (multiplyLeft) {
6629       ierr = DMGetWorkArray(dm,newNumIndices*newNumIndices,MPIU_SCALAR,&newValues);CHKERRQ(ierr);
6630       ierr = PetscArrayzero(newValues,newNumIndices*newNumIndices);CHKERRQ(ierr);
6631       /* multiply constraints transpose on the left */
6632       if (numFields) {
6633         for (f = 0; f < numFields; f++) {
6634           PetscInt oldOff = offsets[f];
6635 
6636           for (p = 0; p < numPoints; p++) {
6637             PetscInt rStart = newPointOffsets[f][p];
6638             PetscInt b      = points[2 * p];
6639             PetscInt c, r, k;
6640             PetscInt dof;
6641 
6642             ierr = PetscSectionGetFieldDof(section,b,f,&dof);CHKERRQ(ierr);
6643             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
6644               PetscInt nRows                        = newPointOffsets[f][p+1]-rStart;
6645               const PetscScalar *PETSC_RESTRICT mat = pointMat[f] + pointMatOffsets[f][p];
6646 
6647               for (r = 0; r < nRows; r++) {
6648                 for (c = 0; c < newNumIndices; c++) {
6649                   for (k = 0; k < dof; k++) {
6650                     newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
6651                   }
6652                 }
6653               }
6654             }
6655             else {
6656               /* copy this row as is */
6657               for (r = 0; r < dof; r++) {
6658                 for (c = 0; c < newNumIndices; c++) {
6659                   newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
6660                 }
6661               }
6662             }
6663             oldOff += dof;
6664           }
6665         }
6666       }
6667       else {
6668         PetscInt oldOff = 0;
6669 
6670         for (p = 0; p < numPoints; p++) {
6671           PetscInt rStart = newPointOffsets[0][p];
6672           PetscInt b      = points[2 * p];
6673           PetscInt c, r, k;
6674           PetscInt dof;
6675 
6676           ierr = PetscSectionGetDof(section,b,&dof);CHKERRQ(ierr);
6677           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
6678             PetscInt nRows                        = newPointOffsets[0][p+1]-rStart;
6679             const PetscScalar *PETSC_RESTRICT mat = pointMat[0] + pointMatOffsets[0][p];
6680 
6681             for (r = 0; r < nRows; r++) {
6682               for (c = 0; c < newNumIndices; c++) {
6683                 for (k = 0; k < dof; k++) {
6684                   newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
6685                 }
6686               }
6687             }
6688           }
6689           else {
6690             /* copy this row as is */
6691             for (r = 0; r < dof; r++) {
6692               for (c = 0; c < newNumIndices; c++) {
6693                 newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
6694               }
6695             }
6696           }
6697           oldOff += dof;
6698         }
6699       }
6700 
6701       ierr = DMRestoreWorkArray(dm,newNumIndices*numIndices,MPIU_SCALAR,&tmpValues);CHKERRQ(ierr);
6702     }
6703     else {
6704       newValues = tmpValues;
6705     }
6706   }
6707 
6708   /* clean up */
6709   ierr = DMRestoreWorkArray(dm,maxDof,MPIU_INT,&indices);CHKERRQ(ierr);
6710   ierr = DMRestoreWorkArray(dm,maxAnchor*maxDof,MPIU_INT,&newIndices);CHKERRQ(ierr);
6711 
6712   if (numFields) {
6713     for (f = 0; f < numFields; f++) {
6714       ierr = DMRestoreWorkArray(dm,pointMatOffsets[f][numPoints],MPIU_SCALAR,&pointMat[f]);CHKERRQ(ierr);
6715       ierr = DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[f]);CHKERRQ(ierr);
6716       ierr = DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[f]);CHKERRQ(ierr);
6717     }
6718   }
6719   else {
6720     ierr = DMRestoreWorkArray(dm,pointMatOffsets[0][numPoints],MPIU_SCALAR,&pointMat[0]);CHKERRQ(ierr);
6721     ierr = DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[0]);CHKERRQ(ierr);
6722     ierr = DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[0]);CHKERRQ(ierr);
6723   }
6724   ierr = ISRestoreIndices(aIS,&anchors);CHKERRQ(ierr);
6725 
6726   /* output */
6727   if (outPoints) {
6728     *outPoints = newPoints;
6729   }
6730   else {
6731     ierr = DMRestoreWorkArray(dm,2*newNumPoints,MPIU_INT,&newPoints);CHKERRQ(ierr);
6732   }
6733   if (outValues) {
6734     *outValues = newValues;
6735   }
6736   for (f = 0; f <= numFields; f++) {
6737     offsets[f] = newOffsets[f];
6738   }
6739   PetscFunctionReturn(0);
6740 }
6741 
6742 /*@C
6743   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
6744 
6745   Not collective
6746 
6747   Input Parameters:
6748 + dm         - The DM
6749 . section    - The PetscSection describing the points (a local section)
6750 . idxSection - The PetscSection from which to obtain indices (may be local or global)
6751 . point      - The point defining the closure
6752 - useClPerm  - Use the closure point permutation if available
6753 
6754   Output Parameters:
6755 + numIndices - The number of dof indices in the closure of point with the input sections
6756 . indices    - The dof indices
6757 . outOffsets - Array to write the field offsets into, or NULL
6758 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
6759 
6760   Notes:
6761   Must call DMPlexRestoreClosureIndices() to free allocated memory
6762 
6763   If idxSection is global, any constrained dofs (see DMAddBoundary(), for example) will get negative indices.  The value
6764   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
6765   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
6766   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
6767   indices (with the above semantics) are implied.
6768 
6769   Level: advanced
6770 
6771 .seealso DMPlexRestoreClosureIndices(), DMPlexVecGetClosure(), DMPlexMatSetClosure(), DMGetLocalSection(), DMGetGlobalSection()
6772 @*/
6773 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm,
6774                                        PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
6775 {
6776   /* Closure ordering */
6777   PetscSection        clSection;
6778   IS                  clPoints;
6779   const PetscInt     *clp;
6780   PetscInt           *points;
6781   const PetscInt     *clperm = NULL;
6782   /* Dof permutation and sign flips */
6783   const PetscInt    **perms[32] = {NULL};
6784   const PetscScalar **flips[32] = {NULL};
6785   PetscScalar        *valCopy   = NULL;
6786   /* Hanging node constraints */
6787   PetscInt           *pointsC = NULL;
6788   PetscScalar        *valuesC = NULL;
6789   PetscInt            NclC, NiC;
6790 
6791   PetscInt           *idx;
6792   PetscInt            Nf, Ncl, Ni = 0, offsets[32], p, f;
6793   PetscBool           isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
6794   PetscErrorCode      ierr;
6795 
6796   PetscFunctionBeginHot;
6797   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6798   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6799   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
6800   if (numIndices) PetscValidPointer(numIndices, 6);
6801   if (indices)    PetscValidPointer(indices, 7);
6802   if (outOffsets) PetscValidPointer(outOffsets, 8);
6803   if (values)     PetscValidPointer(values, 9);
6804   ierr = PetscSectionGetNumFields(section, &Nf);CHKERRQ(ierr);
6805   if (Nf > 31) SETERRQ1(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %D limited to 31", Nf);
6806   ierr = PetscArrayzero(offsets, 32);CHKERRQ(ierr);
6807   /* 1) Get points in closure */
6808   ierr = DMPlexGetCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp);CHKERRQ(ierr);
6809   if (useClPerm) {
6810     PetscInt depth, clsize;
6811     ierr = DMPlexGetPointDepth(dm, point, &depth);CHKERRQ(ierr);
6812     for (clsize=0,p=0; p<Ncl; p++) {
6813       PetscInt dof;
6814       ierr = PetscSectionGetDof(section, points[2*p], &dof);CHKERRQ(ierr);
6815       clsize += dof;
6816     }
6817     ierr = PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, clsize, &clperm);CHKERRQ(ierr);
6818   }
6819   /* 2) Get number of indices on these points and field offsets from section */
6820   for (p = 0; p < Ncl*2; p += 2) {
6821     PetscInt dof, fdof;
6822 
6823     ierr = PetscSectionGetDof(section, points[p], &dof);CHKERRQ(ierr);
6824     for (f = 0; f < Nf; ++f) {
6825       ierr = PetscSectionGetFieldDof(section, points[p], f, &fdof);CHKERRQ(ierr);
6826       offsets[f+1] += fdof;
6827     }
6828     Ni += dof;
6829   }
6830   for (f = 1; f < Nf; ++f) offsets[f+1] += offsets[f];
6831   if (Nf && offsets[Nf] != Ni) SETERRQ2(PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Invalid size for closure %D should be %D", offsets[Nf], Ni);
6832   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
6833   for (f = 0; f < PetscMax(1, Nf); ++f) {
6834     if (Nf) {ierr = PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]);CHKERRQ(ierr);}
6835     else    {ierr = PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]);CHKERRQ(ierr);}
6836     /* may need to apply sign changes to the element matrix */
6837     if (values && flips[f]) {
6838       PetscInt foffset = offsets[f];
6839 
6840       for (p = 0; p < Ncl; ++p) {
6841         PetscInt           pnt  = points[2*p], fdof;
6842         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
6843 
6844         if (!Nf) {ierr = PetscSectionGetDof(section, pnt, &fdof);CHKERRQ(ierr);}
6845         else     {ierr = PetscSectionGetFieldDof(section, pnt, f, &fdof);CHKERRQ(ierr);}
6846         if (flip) {
6847           PetscInt i, j, k;
6848 
6849           if (!valCopy) {
6850             ierr = DMGetWorkArray(dm, Ni*Ni, MPIU_SCALAR, &valCopy);CHKERRQ(ierr);
6851             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
6852             *values = valCopy;
6853           }
6854           for (i = 0; i < fdof; ++i) {
6855             PetscScalar fval = flip[i];
6856 
6857             for (k = 0; k < Ni; ++k) {
6858               valCopy[Ni * (foffset + i) + k] *= fval;
6859               valCopy[Ni * k + (foffset + i)] *= fval;
6860             }
6861           }
6862         }
6863         foffset += fdof;
6864       }
6865     }
6866   }
6867   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
6868   ierr = DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE);CHKERRQ(ierr);
6869   if (NclC) {
6870     if (valCopy) {ierr = DMRestoreWorkArray(dm, Ni*Ni, MPIU_SCALAR, &valCopy);CHKERRQ(ierr);}
6871     for (f = 0; f < PetscMax(1, Nf); ++f) {
6872       if (Nf) {ierr = PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]);CHKERRQ(ierr);}
6873       else    {ierr = PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]);CHKERRQ(ierr);}
6874     }
6875     for (f = 0; f < PetscMax(1, Nf); ++f) {
6876       if (Nf) {ierr = PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]);CHKERRQ(ierr);}
6877       else    {ierr = PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]);CHKERRQ(ierr);}
6878     }
6879     ierr = DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp);CHKERRQ(ierr);
6880     Ncl     = NclC;
6881     Ni      = NiC;
6882     points  = pointsC;
6883     if (values) *values = valuesC;
6884   }
6885   /* 5) Calculate indices */
6886   ierr = DMGetWorkArray(dm, Ni, MPIU_INT, &idx);CHKERRQ(ierr);
6887   if (Nf) {
6888     PetscInt  idxOff;
6889     PetscBool useFieldOffsets;
6890 
6891     if (outOffsets) {for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];}
6892     ierr = PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets);CHKERRQ(ierr);
6893     if (useFieldOffsets) {
6894       for (p = 0; p < Ncl; ++p) {
6895         const PetscInt pnt = points[p*2];
6896 
6897         ierr = DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx);CHKERRQ(ierr);
6898       }
6899     } else {
6900       for (p = 0; p < Ncl; ++p) {
6901         const PetscInt pnt = points[p*2];
6902 
6903         ierr = PetscSectionGetOffset(idxSection, pnt, &idxOff);CHKERRQ(ierr);
6904         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
6905          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
6906          * global section. */
6907         ierr = DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff+1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx);CHKERRQ(ierr);
6908       }
6909     }
6910   } else {
6911     PetscInt off = 0, idxOff;
6912 
6913     for (p = 0; p < Ncl; ++p) {
6914       const PetscInt  pnt  = points[p*2];
6915       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
6916 
6917       ierr = PetscSectionGetOffset(idxSection, pnt, &idxOff);CHKERRQ(ierr);
6918       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
6919        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
6920       ierr = DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff+1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx);CHKERRQ(ierr);
6921     }
6922   }
6923   /* 6) Cleanup */
6924   for (f = 0; f < PetscMax(1, Nf); ++f) {
6925     if (Nf) {ierr = PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]);CHKERRQ(ierr);}
6926     else    {ierr = PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]);CHKERRQ(ierr);}
6927   }
6928   if (NclC) {
6929     ierr = DMRestoreWorkArray(dm, NclC*2, MPIU_INT, &pointsC);CHKERRQ(ierr);
6930   } else {
6931     ierr = DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp);CHKERRQ(ierr);
6932   }
6933 
6934   if (numIndices) *numIndices = Ni;
6935   if (indices)    *indices    = idx;
6936   PetscFunctionReturn(0);
6937 }
6938 
6939 /*@C
6940   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
6941 
6942   Not collective
6943 
6944   Input Parameters:
6945 + dm         - The DM
6946 . section    - The PetscSection describing the points (a local section)
6947 . idxSection - The PetscSection from which to obtain indices (may be local or global)
6948 . point      - The point defining the closure
6949 - useClPerm  - Use the closure point permutation if available
6950 
6951   Output Parameters:
6952 + numIndices - The number of dof indices in the closure of point with the input sections
6953 . indices    - The dof indices
6954 . outOffsets - Array to write the field offsets into, or NULL
6955 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
6956 
6957   Notes:
6958   If values were modified, the user is responsible for calling DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values).
6959 
6960   If idxSection is global, any constrained dofs (see DMAddBoundary(), for example) will get negative indices.  The value
6961   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
6962   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
6963   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
6964   indices (with the above semantics) are implied.
6965 
6966   Level: advanced
6967 
6968 .seealso DMPlexGetClosureIndices(), DMPlexVecGetClosure(), DMPlexMatSetClosure(), DMGetLocalSection(), DMGetGlobalSection()
6969 @*/
6970 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm,
6971                                            PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
6972 {
6973   PetscErrorCode ierr;
6974 
6975   PetscFunctionBegin;
6976   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6977   PetscValidPointer(indices, 7);
6978   ierr = DMRestoreWorkArray(dm, 0, MPIU_INT, indices);CHKERRQ(ierr);
6979   PetscFunctionReturn(0);
6980 }
6981 
6982 /*@C
6983   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
6984 
6985   Not collective
6986 
6987   Input Parameters:
6988 + dm - The DM
6989 . section - The section describing the layout in v, or NULL to use the default section
6990 . globalSection - The section describing the layout in v, or NULL to use the default global section
6991 . A - The matrix
6992 . point - The point in the DM
6993 . values - The array of values
6994 - mode - The insert mode, where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions
6995 
6996   Fortran Notes:
6997   This routine is only available in Fortran 90, and you must include petsc.h90 in your code.
6998 
6999   Level: intermediate
7000 
7001 .seealso DMPlexMatSetClosureGeneral(), DMPlexVecGetClosure(), DMPlexVecSetClosure()
7002 @*/
7003 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7004 {
7005   DM_Plex           *mesh = (DM_Plex*) dm->data;
7006   PetscInt          *indices;
7007   PetscInt           numIndices;
7008   const PetscScalar *valuesOrig = values;
7009   PetscErrorCode     ierr;
7010 
7011   PetscFunctionBegin;
7012   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7013   if (!section) {ierr = DMGetLocalSection(dm, &section);CHKERRQ(ierr);}
7014   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7015   if (!globalSection) {ierr = DMGetGlobalSection(dm, &globalSection);CHKERRQ(ierr);}
7016   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
7017   PetscValidHeaderSpecific(A, MAT_CLASSID, 4);
7018 
7019   ierr = DMPlexGetClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **) &values);CHKERRQ(ierr);
7020 
7021   if (mesh->printSetValues) {ierr = DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values);CHKERRQ(ierr);}
7022   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
7023   if (ierr) {
7024     PetscMPIInt    rank;
7025     PetscErrorCode ierr2;
7026 
7027     ierr2 = MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank);CHKERRMPI(ierr2);
7028     ierr2 = (*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank);CHKERRQ(ierr2);
7029     ierr2 = DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values);CHKERRQ(ierr2);
7030     ierr2 = DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **) &values);CHKERRQ(ierr2);
7031     if (values != valuesOrig) {ierr2 = DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values);CHKERRQ(ierr2);}
7032     CHKERRQ(ierr);
7033   }
7034   if (mesh->printFEM > 1) {
7035     PetscInt i;
7036     ierr = PetscPrintf(PETSC_COMM_SELF, "  Indices:");CHKERRQ(ierr);
7037     for (i = 0; i < numIndices; ++i) {ierr = PetscPrintf(PETSC_COMM_SELF, " %D", indices[i]);CHKERRQ(ierr);}
7038     ierr = PetscPrintf(PETSC_COMM_SELF, "\n");CHKERRQ(ierr);
7039   }
7040 
7041   ierr = DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **) &values);CHKERRQ(ierr);
7042   if (values != valuesOrig) {ierr = DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values);CHKERRQ(ierr);}
7043   PetscFunctionReturn(0);
7044 }
7045 
7046 /*@C
7047   DMPlexMatSetClosure - Set an array of the values on the closure of 'point' using a different row and column section
7048 
7049   Not collective
7050 
7051   Input Parameters:
7052 + dmRow - The DM for the row fields
7053 . sectionRow - The section describing the layout, or NULL to use the default section in dmRow
7054 . globalSectionRow - The section describing the layout, or NULL to use the default global section in dmRow
7055 . dmCol - The DM for the column fields
7056 . sectionCol - The section describing the layout, or NULL to use the default section in dmCol
7057 . globalSectionCol - The section describing the layout, or NULL to use the default global section in dmCol
7058 . A - The matrix
7059 . point - The point in the DMs
7060 . values - The array of values
7061 - mode - The insert mode, where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions
7062 
7063   Level: intermediate
7064 
7065 .seealso DMPlexMatSetClosure(), DMPlexVecGetClosure(), DMPlexVecSetClosure()
7066 @*/
7067 PetscErrorCode DMPlexMatSetClosureGeneral(DM dmRow, PetscSection sectionRow, PetscSection globalSectionRow, DM dmCol, PetscSection sectionCol, PetscSection globalSectionCol, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7068 {
7069   DM_Plex           *mesh = (DM_Plex*) dmRow->data;
7070   PetscInt          *indicesRow, *indicesCol;
7071   PetscInt           numIndicesRow, numIndicesCol;
7072   const PetscScalar *valuesOrig = values;
7073   PetscErrorCode     ierr;
7074 
7075   PetscFunctionBegin;
7076   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
7077   if (!sectionRow) {ierr = DMGetLocalSection(dmRow, &sectionRow);CHKERRQ(ierr);}
7078   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
7079   if (!globalSectionRow) {ierr = DMGetGlobalSection(dmRow, &globalSectionRow);CHKERRQ(ierr);}
7080   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
7081   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 4);
7082   if (!sectionCol) {ierr = DMGetLocalSection(dmCol, &sectionCol);CHKERRQ(ierr);}
7083   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 5);
7084   if (!globalSectionCol) {ierr = DMGetGlobalSection(dmCol, &globalSectionCol);CHKERRQ(ierr);}
7085   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 6);
7086   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7087 
7088   ierr = DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values);CHKERRQ(ierr);
7089   ierr = DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **) &values);CHKERRQ(ierr);
7090 
7091   if (mesh->printSetValues) {ierr = DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values);CHKERRQ(ierr);}
7092   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
7093   if (ierr) {
7094     PetscMPIInt    rank;
7095     PetscErrorCode ierr2;
7096 
7097     ierr2 = MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank);CHKERRMPI(ierr2);
7098     ierr2 = (*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank);CHKERRQ(ierr2);
7099     ierr2 = DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values);CHKERRQ(ierr2);
7100     ierr2 = DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values);CHKERRQ(ierr2);
7101     ierr2 = DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **) &values);CHKERRQ(ierr2);
7102     if (values != valuesOrig) {ierr2 = DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values);CHKERRQ(ierr2);}
7103     CHKERRQ(ierr);
7104   }
7105 
7106   ierr = DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values);CHKERRQ(ierr);
7107   ierr = DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **) &values);CHKERRQ(ierr);
7108   if (values != valuesOrig) {ierr = DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values);CHKERRQ(ierr);}
7109   PetscFunctionReturn(0);
7110 }
7111 
7112 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7113 {
7114   DM_Plex        *mesh   = (DM_Plex*) dmf->data;
7115   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7116   PetscInt       *cpoints = NULL;
7117   PetscInt       *findices, *cindices;
7118   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7119   PetscInt        foffsets[32], coffsets[32];
7120   DMPolytopeType  ct;
7121   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7122   PetscErrorCode  ierr;
7123 
7124   PetscFunctionBegin;
7125   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7126   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7127   if (!fsection) {ierr = DMGetLocalSection(dmf, &fsection);CHKERRQ(ierr);}
7128   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7129   if (!csection) {ierr = DMGetLocalSection(dmc, &csection);CHKERRQ(ierr);}
7130   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7131   if (!globalFSection) {ierr = DMGetGlobalSection(dmf, &globalFSection);CHKERRQ(ierr);}
7132   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7133   if (!globalCSection) {ierr = DMGetGlobalSection(dmc, &globalCSection);CHKERRQ(ierr);}
7134   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7135   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7136   ierr = PetscSectionGetNumFields(fsection, &numFields);CHKERRQ(ierr);
7137   if (numFields > 31) SETERRQ1(PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %D limited to 31", numFields);
7138   ierr = PetscArrayzero(foffsets, 32);CHKERRQ(ierr);
7139   ierr = PetscArrayzero(coffsets, 32);CHKERRQ(ierr);
7140   /* Column indices */
7141   ierr = DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints);CHKERRQ(ierr);
7142   maxFPoints = numCPoints;
7143   /* Compress out points not in the section */
7144   /*   TODO: Squeeze out points with 0 dof as well */
7145   ierr = PetscSectionGetChart(csection, &pStart, &pEnd);CHKERRQ(ierr);
7146   for (p = 0, q = 0; p < numCPoints*2; p += 2) {
7147     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7148       cpoints[q*2]   = cpoints[p];
7149       cpoints[q*2+1] = cpoints[p+1];
7150       ++q;
7151     }
7152   }
7153   numCPoints = q;
7154   for (p = 0, numCIndices = 0; p < numCPoints*2; p += 2) {
7155     PetscInt fdof;
7156 
7157     ierr = PetscSectionGetDof(csection, cpoints[p], &dof);CHKERRQ(ierr);
7158     if (!dof) continue;
7159     for (f = 0; f < numFields; ++f) {
7160       ierr           = PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof);CHKERRQ(ierr);
7161       coffsets[f+1] += fdof;
7162     }
7163     numCIndices += dof;
7164   }
7165   for (f = 1; f < numFields; ++f) coffsets[f+1] += coffsets[f];
7166   /* Row indices */
7167   ierr = DMPlexGetCellType(dmc, point, &ct);CHKERRQ(ierr);
7168   {
7169     DMPlexCellRefiner cr;
7170     ierr = DMPlexCellRefinerCreate(dmc, &cr);CHKERRQ(ierr);
7171     ierr = DMPlexCellRefinerGetAffineTransforms(cr, ct, &numSubcells, NULL, NULL, NULL);CHKERRQ(ierr);
7172     ierr = DMPlexCellRefinerDestroy(&cr);CHKERRQ(ierr);
7173   }
7174   ierr = DMGetWorkArray(dmf, maxFPoints*2*numSubcells, MPIU_INT, &ftotpoints);CHKERRQ(ierr);
7175   for (r = 0, q = 0; r < numSubcells; ++r) {
7176     /* TODO Map from coarse to fine cells */
7177     ierr = DMPlexGetTransitiveClosure(dmf, point*numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints);CHKERRQ(ierr);
7178     /* Compress out points not in the section */
7179     ierr = PetscSectionGetChart(fsection, &pStart, &pEnd);CHKERRQ(ierr);
7180     for (p = 0; p < numFPoints*2; p += 2) {
7181       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7182         ierr = PetscSectionGetDof(fsection, fpoints[p], &dof);CHKERRQ(ierr);
7183         if (!dof) continue;
7184         for (s = 0; s < q; ++s) if (fpoints[p] == ftotpoints[s*2]) break;
7185         if (s < q) continue;
7186         ftotpoints[q*2]   = fpoints[p];
7187         ftotpoints[q*2+1] = fpoints[p+1];
7188         ++q;
7189       }
7190     }
7191     ierr = DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints);CHKERRQ(ierr);
7192   }
7193   numFPoints = q;
7194   for (p = 0, numFIndices = 0; p < numFPoints*2; p += 2) {
7195     PetscInt fdof;
7196 
7197     ierr = PetscSectionGetDof(fsection, ftotpoints[p], &dof);CHKERRQ(ierr);
7198     if (!dof) continue;
7199     for (f = 0; f < numFields; ++f) {
7200       ierr           = PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof);CHKERRQ(ierr);
7201       foffsets[f+1] += fdof;
7202     }
7203     numFIndices += dof;
7204   }
7205   for (f = 1; f < numFields; ++f) foffsets[f+1] += foffsets[f];
7206 
7207   if (numFields && foffsets[numFields] != numFIndices) SETERRQ2(PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %D should be %D", foffsets[numFields], numFIndices);
7208   if (numFields && coffsets[numFields] != numCIndices) SETERRQ2(PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %D should be %D", coffsets[numFields], numCIndices);
7209   ierr = DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices);CHKERRQ(ierr);
7210   ierr = DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices);CHKERRQ(ierr);
7211   if (numFields) {
7212     const PetscInt **permsF[32] = {NULL};
7213     const PetscInt **permsC[32] = {NULL};
7214 
7215     for (f = 0; f < numFields; f++) {
7216       ierr = PetscSectionGetFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL);CHKERRQ(ierr);
7217       ierr = PetscSectionGetFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL);CHKERRQ(ierr);
7218     }
7219     for (p = 0; p < numFPoints; p++) {
7220       ierr = PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff);CHKERRQ(ierr);
7221       ierr = DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices);CHKERRQ(ierr);
7222     }
7223     for (p = 0; p < numCPoints; p++) {
7224       ierr = PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff);CHKERRQ(ierr);
7225       ierr = DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices);CHKERRQ(ierr);
7226     }
7227     for (f = 0; f < numFields; f++) {
7228       ierr = PetscSectionRestoreFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL);CHKERRQ(ierr);
7229       ierr = PetscSectionRestoreFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL);CHKERRQ(ierr);
7230     }
7231   } else {
7232     const PetscInt **permsF = NULL;
7233     const PetscInt **permsC = NULL;
7234 
7235     ierr = PetscSectionGetPointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL);CHKERRQ(ierr);
7236     ierr = PetscSectionGetPointSyms(csection,numCPoints,cpoints,&permsC,NULL);CHKERRQ(ierr);
7237     for (p = 0, off = 0; p < numFPoints; p++) {
7238       const PetscInt *perm = permsF ? permsF[p] : NULL;
7239 
7240       ierr = PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff);CHKERRQ(ierr);
7241       ierr = DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices);CHKERRQ(ierr);
7242     }
7243     for (p = 0, off = 0; p < numCPoints; p++) {
7244       const PetscInt *perm = permsC ? permsC[p] : NULL;
7245 
7246       ierr = PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff);CHKERRQ(ierr);
7247       ierr = DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices);CHKERRQ(ierr);
7248     }
7249     ierr = PetscSectionRestorePointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL);CHKERRQ(ierr);
7250     ierr = PetscSectionRestorePointSyms(csection,numCPoints,cpoints,&permsC,NULL);CHKERRQ(ierr);
7251   }
7252   if (mesh->printSetValues) {ierr = DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values);CHKERRQ(ierr);}
7253   /* TODO: flips */
7254   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
7255   if (ierr) {
7256     PetscMPIInt    rank;
7257     PetscErrorCode ierr2;
7258 
7259     ierr2 = MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank);CHKERRMPI(ierr2);
7260     ierr2 = (*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank);CHKERRQ(ierr2);
7261     ierr2 = DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values);CHKERRQ(ierr2);
7262     ierr2 = DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices);CHKERRQ(ierr2);
7263     ierr2 = DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices);CHKERRQ(ierr2);
7264     CHKERRQ(ierr);
7265   }
7266   ierr = DMRestoreWorkArray(dmf, numCPoints*2*4, MPIU_INT, &ftotpoints);CHKERRQ(ierr);
7267   ierr = DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints);CHKERRQ(ierr);
7268   ierr = DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices);CHKERRQ(ierr);
7269   ierr = DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices);CHKERRQ(ierr);
7270   PetscFunctionReturn(0);
7271 }
7272 
7273 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
7274 {
7275   PetscInt      *fpoints = NULL, *ftotpoints = NULL;
7276   PetscInt      *cpoints = NULL;
7277   PetscInt       foffsets[32], coffsets[32];
7278   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7279   DMPolytopeType ct;
7280   PetscInt       numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7281   PetscErrorCode ierr;
7282 
7283   PetscFunctionBegin;
7284   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7285   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7286   if (!fsection) {ierr = DMGetLocalSection(dmf, &fsection);CHKERRQ(ierr);}
7287   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7288   if (!csection) {ierr = DMGetLocalSection(dmc, &csection);CHKERRQ(ierr);}
7289   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7290   if (!globalFSection) {ierr = DMGetGlobalSection(dmf, &globalFSection);CHKERRQ(ierr);}
7291   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7292   if (!globalCSection) {ierr = DMGetGlobalSection(dmc, &globalCSection);CHKERRQ(ierr);}
7293   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7294   ierr = PetscSectionGetNumFields(fsection, &numFields);CHKERRQ(ierr);
7295   if (numFields > 31) SETERRQ1(PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %D limited to 31", numFields);
7296   ierr = PetscArrayzero(foffsets, 32);CHKERRQ(ierr);
7297   ierr = PetscArrayzero(coffsets, 32);CHKERRQ(ierr);
7298   /* Column indices */
7299   ierr = DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints);CHKERRQ(ierr);
7300   maxFPoints = numCPoints;
7301   /* Compress out points not in the section */
7302   /*   TODO: Squeeze out points with 0 dof as well */
7303   ierr = PetscSectionGetChart(csection, &pStart, &pEnd);CHKERRQ(ierr);
7304   for (p = 0, q = 0; p < numCPoints*2; p += 2) {
7305     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7306       cpoints[q*2]   = cpoints[p];
7307       cpoints[q*2+1] = cpoints[p+1];
7308       ++q;
7309     }
7310   }
7311   numCPoints = q;
7312   for (p = 0, numCIndices = 0; p < numCPoints*2; p += 2) {
7313     PetscInt fdof;
7314 
7315     ierr = PetscSectionGetDof(csection, cpoints[p], &dof);CHKERRQ(ierr);
7316     if (!dof) continue;
7317     for (f = 0; f < numFields; ++f) {
7318       ierr           = PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof);CHKERRQ(ierr);
7319       coffsets[f+1] += fdof;
7320     }
7321     numCIndices += dof;
7322   }
7323   for (f = 1; f < numFields; ++f) coffsets[f+1] += coffsets[f];
7324   /* Row indices */
7325   ierr = DMPlexGetCellType(dmc, point, &ct);CHKERRQ(ierr);
7326   {
7327     DMPlexCellRefiner cr;
7328     ierr = DMPlexCellRefinerCreate(dmc, &cr);CHKERRQ(ierr);
7329     ierr = DMPlexCellRefinerGetAffineTransforms(cr, ct, &numSubcells, NULL, NULL, NULL);CHKERRQ(ierr);
7330     ierr = DMPlexCellRefinerDestroy(&cr);CHKERRQ(ierr);
7331   }
7332   ierr = DMGetWorkArray(dmf, maxFPoints*2*numSubcells, MPIU_INT, &ftotpoints);CHKERRQ(ierr);
7333   for (r = 0, q = 0; r < numSubcells; ++r) {
7334     /* TODO Map from coarse to fine cells */
7335     ierr = DMPlexGetTransitiveClosure(dmf, point*numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints);CHKERRQ(ierr);
7336     /* Compress out points not in the section */
7337     ierr = PetscSectionGetChart(fsection, &pStart, &pEnd);CHKERRQ(ierr);
7338     for (p = 0; p < numFPoints*2; p += 2) {
7339       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7340         ierr = PetscSectionGetDof(fsection, fpoints[p], &dof);CHKERRQ(ierr);
7341         if (!dof) continue;
7342         for (s = 0; s < q; ++s) if (fpoints[p] == ftotpoints[s*2]) break;
7343         if (s < q) continue;
7344         ftotpoints[q*2]   = fpoints[p];
7345         ftotpoints[q*2+1] = fpoints[p+1];
7346         ++q;
7347       }
7348     }
7349     ierr = DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints);CHKERRQ(ierr);
7350   }
7351   numFPoints = q;
7352   for (p = 0, numFIndices = 0; p < numFPoints*2; p += 2) {
7353     PetscInt fdof;
7354 
7355     ierr = PetscSectionGetDof(fsection, ftotpoints[p], &dof);CHKERRQ(ierr);
7356     if (!dof) continue;
7357     for (f = 0; f < numFields; ++f) {
7358       ierr           = PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof);CHKERRQ(ierr);
7359       foffsets[f+1] += fdof;
7360     }
7361     numFIndices += dof;
7362   }
7363   for (f = 1; f < numFields; ++f) foffsets[f+1] += foffsets[f];
7364 
7365   if (numFields && foffsets[numFields] != numFIndices) SETERRQ2(PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %D should be %D", foffsets[numFields], numFIndices);
7366   if (numFields && coffsets[numFields] != numCIndices) SETERRQ2(PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %D should be %D", coffsets[numFields], numCIndices);
7367   if (numFields) {
7368     const PetscInt **permsF[32] = {NULL};
7369     const PetscInt **permsC[32] = {NULL};
7370 
7371     for (f = 0; f < numFields; f++) {
7372       ierr = PetscSectionGetFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL);CHKERRQ(ierr);
7373       ierr = PetscSectionGetFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL);CHKERRQ(ierr);
7374     }
7375     for (p = 0; p < numFPoints; p++) {
7376       ierr = PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff);CHKERRQ(ierr);
7377       ierr = DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices);CHKERRQ(ierr);
7378     }
7379     for (p = 0; p < numCPoints; p++) {
7380       ierr = PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff);CHKERRQ(ierr);
7381       ierr = DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices);CHKERRQ(ierr);
7382     }
7383     for (f = 0; f < numFields; f++) {
7384       ierr = PetscSectionRestoreFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL);CHKERRQ(ierr);
7385       ierr = PetscSectionRestoreFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL);CHKERRQ(ierr);
7386     }
7387   } else {
7388     const PetscInt **permsF = NULL;
7389     const PetscInt **permsC = NULL;
7390 
7391     ierr = PetscSectionGetPointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL);CHKERRQ(ierr);
7392     ierr = PetscSectionGetPointSyms(csection,numCPoints,cpoints,&permsC,NULL);CHKERRQ(ierr);
7393     for (p = 0, off = 0; p < numFPoints; p++) {
7394       const PetscInt *perm = permsF ? permsF[p] : NULL;
7395 
7396       ierr = PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff);CHKERRQ(ierr);
7397       ierr = DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices);CHKERRQ(ierr);
7398     }
7399     for (p = 0, off = 0; p < numCPoints; p++) {
7400       const PetscInt *perm = permsC ? permsC[p] : NULL;
7401 
7402       ierr = PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff);CHKERRQ(ierr);
7403       ierr = DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices);CHKERRQ(ierr);
7404     }
7405     ierr = PetscSectionRestorePointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL);CHKERRQ(ierr);
7406     ierr = PetscSectionRestorePointSyms(csection,numCPoints,cpoints,&permsC,NULL);CHKERRQ(ierr);
7407   }
7408   ierr = DMRestoreWorkArray(dmf, numCPoints*2*4, MPIU_INT, &ftotpoints);CHKERRQ(ierr);
7409   ierr = DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints);CHKERRQ(ierr);
7410   PetscFunctionReturn(0);
7411 }
7412 
7413 /*@C
7414   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
7415 
7416   Input Parameter:
7417 . dm   - The DMPlex object
7418 
7419   Output Parameter:
7420 . cellHeight - The height of a cell
7421 
7422   Level: developer
7423 
7424 .seealso DMPlexSetVTKCellHeight()
7425 @*/
7426 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
7427 {
7428   DM_Plex *mesh = (DM_Plex*) dm->data;
7429 
7430   PetscFunctionBegin;
7431   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7432   PetscValidPointer(cellHeight, 2);
7433   *cellHeight = mesh->vtkCellHeight;
7434   PetscFunctionReturn(0);
7435 }
7436 
7437 /*@C
7438   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
7439 
7440   Input Parameters:
7441 + dm   - The DMPlex object
7442 - cellHeight - The height of a cell
7443 
7444   Level: developer
7445 
7446 .seealso DMPlexGetVTKCellHeight()
7447 @*/
7448 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
7449 {
7450   DM_Plex *mesh = (DM_Plex*) dm->data;
7451 
7452   PetscFunctionBegin;
7453   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7454   mesh->vtkCellHeight = cellHeight;
7455   PetscFunctionReturn(0);
7456 }
7457 
7458 /*@
7459   DMPlexGetGhostCellStratum - Get the range of cells which are used to enforce FV boundary conditions
7460 
7461   Input Parameter:
7462 . dm - The DMPlex object
7463 
7464   Output Parameters:
7465 + gcStart - The first ghost cell, or NULL
7466 - gcEnd   - The upper bound on ghost cells, or NULL
7467 
7468   Level: advanced
7469 
7470 .seealso DMPlexConstructGhostCells(), DMPlexGetGhostCellStratum()
7471 @*/
7472 PetscErrorCode DMPlexGetGhostCellStratum(DM dm, PetscInt *gcStart, PetscInt *gcEnd)
7473 {
7474   DMLabel        ctLabel;
7475   PetscErrorCode ierr;
7476 
7477   PetscFunctionBegin;
7478   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7479   ierr = DMPlexGetCellTypeLabel(dm, &ctLabel);CHKERRQ(ierr);
7480   ierr = DMLabelGetStratumBounds(ctLabel, DM_POLYTOPE_FV_GHOST, gcStart, gcEnd);CHKERRQ(ierr);
7481   PetscFunctionReturn(0);
7482 }
7483 
7484 /* We can easily have a form that takes an IS instead */
7485 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
7486 {
7487   PetscSection   section, globalSection;
7488   PetscInt      *numbers, p;
7489   PetscErrorCode ierr;
7490 
7491   PetscFunctionBegin;
7492   ierr = PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section);CHKERRQ(ierr);
7493   ierr = PetscSectionSetChart(section, pStart, pEnd);CHKERRQ(ierr);
7494   for (p = pStart; p < pEnd; ++p) {
7495     ierr = PetscSectionSetDof(section, p, 1);CHKERRQ(ierr);
7496   }
7497   ierr = PetscSectionSetUp(section);CHKERRQ(ierr);
7498   ierr = PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection);CHKERRQ(ierr);
7499   ierr = PetscMalloc1(pEnd - pStart, &numbers);CHKERRQ(ierr);
7500   for (p = pStart; p < pEnd; ++p) {
7501     ierr = PetscSectionGetOffset(globalSection, p, &numbers[p-pStart]);CHKERRQ(ierr);
7502     if (numbers[p-pStart] < 0) numbers[p-pStart] -= shift;
7503     else                       numbers[p-pStart] += shift;
7504   }
7505   ierr = ISCreateGeneral(PetscObjectComm((PetscObject) dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering);CHKERRQ(ierr);
7506   if (globalSize) {
7507     PetscLayout layout;
7508     ierr = PetscSectionGetPointLayout(PetscObjectComm((PetscObject) dm), globalSection, &layout);CHKERRQ(ierr);
7509     ierr = PetscLayoutGetSize(layout, globalSize);CHKERRQ(ierr);
7510     ierr = PetscLayoutDestroy(&layout);CHKERRQ(ierr);
7511   }
7512   ierr = PetscSectionDestroy(&section);CHKERRQ(ierr);
7513   ierr = PetscSectionDestroy(&globalSection);CHKERRQ(ierr);
7514   PetscFunctionReturn(0);
7515 }
7516 
7517 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
7518 {
7519   PetscInt       cellHeight, cStart, cEnd;
7520   PetscErrorCode ierr;
7521 
7522   PetscFunctionBegin;
7523   ierr = DMPlexGetVTKCellHeight(dm, &cellHeight);CHKERRQ(ierr);
7524   if (includeHybrid) {ierr = DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd);CHKERRQ(ierr);}
7525   else               {ierr = DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd);CHKERRQ(ierr);}
7526   ierr = DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers);CHKERRQ(ierr);
7527   PetscFunctionReturn(0);
7528 }
7529 
7530 /*@
7531   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
7532 
7533   Input Parameter:
7534 . dm   - The DMPlex object
7535 
7536   Output Parameter:
7537 . globalCellNumbers - Global cell numbers for all cells on this process
7538 
7539   Level: developer
7540 
7541 .seealso DMPlexGetVertexNumbering()
7542 @*/
7543 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
7544 {
7545   DM_Plex       *mesh = (DM_Plex*) dm->data;
7546   PetscErrorCode ierr;
7547 
7548   PetscFunctionBegin;
7549   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7550   if (!mesh->globalCellNumbers) {ierr = DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers);CHKERRQ(ierr);}
7551   *globalCellNumbers = mesh->globalCellNumbers;
7552   PetscFunctionReturn(0);
7553 }
7554 
7555 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
7556 {
7557   PetscInt       vStart, vEnd;
7558   PetscErrorCode ierr;
7559 
7560   PetscFunctionBegin;
7561   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7562   ierr = DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd);CHKERRQ(ierr);
7563   ierr = DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers);CHKERRQ(ierr);
7564   PetscFunctionReturn(0);
7565 }
7566 
7567 /*@
7568   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
7569 
7570   Input Parameter:
7571 . dm   - The DMPlex object
7572 
7573   Output Parameter:
7574 . globalVertexNumbers - Global vertex numbers for all vertices on this process
7575 
7576   Level: developer
7577 
7578 .seealso DMPlexGetCellNumbering()
7579 @*/
7580 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
7581 {
7582   DM_Plex       *mesh = (DM_Plex*) dm->data;
7583   PetscErrorCode ierr;
7584 
7585   PetscFunctionBegin;
7586   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7587   if (!mesh->globalVertexNumbers) {ierr = DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers);CHKERRQ(ierr);}
7588   *globalVertexNumbers = mesh->globalVertexNumbers;
7589   PetscFunctionReturn(0);
7590 }
7591 
7592 /*@
7593   DMPlexCreatePointNumbering - Create a global numbering for all points on this process
7594 
7595   Input Parameter:
7596 . dm   - The DMPlex object
7597 
7598   Output Parameter:
7599 . globalPointNumbers - Global numbers for all points on this process
7600 
7601   Level: developer
7602 
7603 .seealso DMPlexGetCellNumbering()
7604 @*/
7605 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
7606 {
7607   IS             nums[4];
7608   PetscInt       depths[4], gdepths[4], starts[4];
7609   PetscInt       depth, d, shift = 0;
7610   PetscErrorCode ierr;
7611 
7612   PetscFunctionBegin;
7613   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7614   ierr = DMPlexGetDepth(dm, &depth);CHKERRQ(ierr);
7615   /* For unstratified meshes use dim instead of depth */
7616   if (depth < 0) {ierr = DMGetDimension(dm, &depth);CHKERRQ(ierr);}
7617   for (d = 0; d <= depth; ++d) {
7618     PetscInt end;
7619 
7620     depths[d] = depth-d;
7621     ierr = DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end);CHKERRQ(ierr);
7622     if (!(starts[d]-end)) { starts[d] = depths[d] = -1; }
7623   }
7624   ierr = PetscSortIntWithArray(depth+1, starts, depths);CHKERRQ(ierr);
7625   ierr = MPIU_Allreduce(depths, gdepths, depth+1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject) dm));CHKERRMPI(ierr);
7626   for (d = 0; d <= depth; ++d) {
7627     if (starts[d] >= 0 && depths[d] != gdepths[d]) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_PLIB,"Expected depth %D, found %D",depths[d],gdepths[d]);
7628   }
7629   for (d = 0; d <= depth; ++d) {
7630     PetscInt pStart, pEnd, gsize;
7631 
7632     ierr = DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd);CHKERRQ(ierr);
7633     ierr = DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]);CHKERRQ(ierr);
7634     shift += gsize;
7635   }
7636   ierr = ISConcatenate(PetscObjectComm((PetscObject) dm), depth+1, nums, globalPointNumbers);CHKERRQ(ierr);
7637   for (d = 0; d <= depth; ++d) {ierr = ISDestroy(&nums[d]);CHKERRQ(ierr);}
7638   PetscFunctionReturn(0);
7639 }
7640 
7641 /*@
7642   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
7643 
7644   Input Parameter:
7645 . dm - The DMPlex object
7646 
7647   Output Parameter:
7648 . ranks - The rank field
7649 
7650   Options Database Keys:
7651 . -dm_partition_view - Adds the rank field into the DM output from -dm_view using the same viewer
7652 
7653   Level: intermediate
7654 
7655 .seealso: DMView()
7656 @*/
7657 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
7658 {
7659   DM             rdm;
7660   PetscFE        fe;
7661   PetscScalar   *r;
7662   PetscMPIInt    rank;
7663   DMPolytopeType ct;
7664   PetscInt       dim, cStart, cEnd, c;
7665   PetscBool      simplex;
7666   PetscErrorCode ierr;
7667 
7668   PetscFunctionBeginUser;
7669   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7670   PetscValidPointer(ranks, 2);
7671   ierr = MPI_Comm_rank(PetscObjectComm((PetscObject) dm), &rank);CHKERRMPI(ierr);
7672   ierr = DMClone(dm, &rdm);CHKERRQ(ierr);
7673   ierr = DMGetDimension(rdm, &dim);CHKERRQ(ierr);
7674   ierr = DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd);CHKERRQ(ierr);
7675   ierr = DMPlexGetCellType(dm, cStart, &ct);CHKERRQ(ierr);
7676   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct)+1 ? PETSC_TRUE : PETSC_FALSE;
7677   ierr = PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe);CHKERRQ(ierr);
7678   ierr = PetscObjectSetName((PetscObject) fe, "rank");CHKERRQ(ierr);
7679   ierr = DMSetField(rdm, 0, NULL, (PetscObject) fe);CHKERRQ(ierr);
7680   ierr = PetscFEDestroy(&fe);CHKERRQ(ierr);
7681   ierr = DMCreateDS(rdm);CHKERRQ(ierr);
7682   ierr = DMCreateGlobalVector(rdm, ranks);CHKERRQ(ierr);
7683   ierr = PetscObjectSetName((PetscObject) *ranks, "partition");CHKERRQ(ierr);
7684   ierr = VecGetArray(*ranks, &r);CHKERRQ(ierr);
7685   for (c = cStart; c < cEnd; ++c) {
7686     PetscScalar *lr;
7687 
7688     ierr = DMPlexPointGlobalRef(rdm, c, r, &lr);CHKERRQ(ierr);
7689     if (lr) *lr = rank;
7690   }
7691   ierr = VecRestoreArray(*ranks, &r);CHKERRQ(ierr);
7692   ierr = DMDestroy(&rdm);CHKERRQ(ierr);
7693   PetscFunctionReturn(0);
7694 }
7695 
7696 /*@
7697   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
7698 
7699   Input Parameters:
7700 + dm    - The DMPlex
7701 - label - The DMLabel
7702 
7703   Output Parameter:
7704 . val - The label value field
7705 
7706   Options Database Keys:
7707 . -dm_label_view - Adds the label value field into the DM output from -dm_view using the same viewer
7708 
7709   Level: intermediate
7710 
7711 .seealso: DMView()
7712 @*/
7713 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
7714 {
7715   DM             rdm;
7716   PetscFE        fe;
7717   PetscScalar   *v;
7718   PetscInt       dim, cStart, cEnd, c;
7719   PetscErrorCode ierr;
7720 
7721   PetscFunctionBeginUser;
7722   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7723   PetscValidPointer(label, 2);
7724   PetscValidPointer(val, 3);
7725   ierr = DMClone(dm, &rdm);CHKERRQ(ierr);
7726   ierr = DMGetDimension(rdm, &dim);CHKERRQ(ierr);
7727   ierr = PetscFECreateDefault(PetscObjectComm((PetscObject) rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe);CHKERRQ(ierr);
7728   ierr = PetscObjectSetName((PetscObject) fe, "label_value");CHKERRQ(ierr);
7729   ierr = DMSetField(rdm, 0, NULL, (PetscObject) fe);CHKERRQ(ierr);
7730   ierr = PetscFEDestroy(&fe);CHKERRQ(ierr);
7731   ierr = DMCreateDS(rdm);CHKERRQ(ierr);
7732   ierr = DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd);CHKERRQ(ierr);
7733   ierr = DMCreateGlobalVector(rdm, val);CHKERRQ(ierr);
7734   ierr = PetscObjectSetName((PetscObject) *val, "label_value");CHKERRQ(ierr);
7735   ierr = VecGetArray(*val, &v);CHKERRQ(ierr);
7736   for (c = cStart; c < cEnd; ++c) {
7737     PetscScalar *lv;
7738     PetscInt     cval;
7739 
7740     ierr = DMPlexPointGlobalRef(rdm, c, v, &lv);CHKERRQ(ierr);
7741     ierr = DMLabelGetValue(label, c, &cval);CHKERRQ(ierr);
7742     *lv = cval;
7743   }
7744   ierr = VecRestoreArray(*val, &v);CHKERRQ(ierr);
7745   ierr = DMDestroy(&rdm);CHKERRQ(ierr);
7746   PetscFunctionReturn(0);
7747 }
7748 
7749 /*@
7750   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
7751 
7752   Input Parameter:
7753 . dm - The DMPlex object
7754 
7755   Notes:
7756   This is a useful diagnostic when creating meshes programmatically.
7757 
7758   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
7759 
7760   Level: developer
7761 
7762 .seealso: DMCreate(), DMSetFromOptions()
7763 @*/
7764 PetscErrorCode DMPlexCheckSymmetry(DM dm)
7765 {
7766   PetscSection    coneSection, supportSection;
7767   const PetscInt *cone, *support;
7768   PetscInt        coneSize, c, supportSize, s;
7769   PetscInt        pStart, pEnd, p, pp, csize, ssize;
7770   PetscBool       storagecheck = PETSC_TRUE;
7771   PetscErrorCode  ierr;
7772 
7773   PetscFunctionBegin;
7774   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7775   ierr = DMViewFromOptions(dm, NULL, "-sym_dm_view");CHKERRQ(ierr);
7776   ierr = DMPlexGetConeSection(dm, &coneSection);CHKERRQ(ierr);
7777   ierr = DMPlexGetSupportSection(dm, &supportSection);CHKERRQ(ierr);
7778   /* Check that point p is found in the support of its cone points, and vice versa */
7779   ierr = DMPlexGetChart(dm, &pStart, &pEnd);CHKERRQ(ierr);
7780   for (p = pStart; p < pEnd; ++p) {
7781     ierr = DMPlexGetConeSize(dm, p, &coneSize);CHKERRQ(ierr);
7782     ierr = DMPlexGetCone(dm, p, &cone);CHKERRQ(ierr);
7783     for (c = 0; c < coneSize; ++c) {
7784       PetscBool dup = PETSC_FALSE;
7785       PetscInt  d;
7786       for (d = c-1; d >= 0; --d) {
7787         if (cone[c] == cone[d]) {dup = PETSC_TRUE; break;}
7788       }
7789       ierr = DMPlexGetSupportSize(dm, cone[c], &supportSize);CHKERRQ(ierr);
7790       ierr = DMPlexGetSupport(dm, cone[c], &support);CHKERRQ(ierr);
7791       for (s = 0; s < supportSize; ++s) {
7792         if (support[s] == p) break;
7793       }
7794       if ((s >= supportSize) || (dup && (support[s+1] != p))) {
7795         ierr = PetscPrintf(PETSC_COMM_SELF, "p: %D cone: ", p);CHKERRQ(ierr);
7796         for (s = 0; s < coneSize; ++s) {
7797           ierr = PetscPrintf(PETSC_COMM_SELF, "%D, ", cone[s]);CHKERRQ(ierr);
7798         }
7799         ierr = PetscPrintf(PETSC_COMM_SELF, "\n");CHKERRQ(ierr);
7800         ierr = PetscPrintf(PETSC_COMM_SELF, "p: %D support: ", cone[c]);CHKERRQ(ierr);
7801         for (s = 0; s < supportSize; ++s) {
7802           ierr = PetscPrintf(PETSC_COMM_SELF, "%D, ", support[s]);CHKERRQ(ierr);
7803         }
7804         ierr = PetscPrintf(PETSC_COMM_SELF, "\n");CHKERRQ(ierr);
7805         if (dup) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %D not repeatedly found in support of repeated cone point %D", p, cone[c]);
7806         else SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %D not found in support of cone point %D", p, cone[c]);
7807       }
7808     }
7809     ierr = DMPlexGetTreeParent(dm, p, &pp, NULL);CHKERRQ(ierr);
7810     if (p != pp) { storagecheck = PETSC_FALSE; continue; }
7811     ierr = DMPlexGetSupportSize(dm, p, &supportSize);CHKERRQ(ierr);
7812     ierr = DMPlexGetSupport(dm, p, &support);CHKERRQ(ierr);
7813     for (s = 0; s < supportSize; ++s) {
7814       ierr = DMPlexGetConeSize(dm, support[s], &coneSize);CHKERRQ(ierr);
7815       ierr = DMPlexGetCone(dm, support[s], &cone);CHKERRQ(ierr);
7816       for (c = 0; c < coneSize; ++c) {
7817         ierr = DMPlexGetTreeParent(dm, cone[c], &pp, NULL);CHKERRQ(ierr);
7818         if (cone[c] != pp) { c = 0; break; }
7819         if (cone[c] == p) break;
7820       }
7821       if (c >= coneSize) {
7822         ierr = PetscPrintf(PETSC_COMM_SELF, "p: %D support: ", p);CHKERRQ(ierr);
7823         for (c = 0; c < supportSize; ++c) {
7824           ierr = PetscPrintf(PETSC_COMM_SELF, "%D, ", support[c]);CHKERRQ(ierr);
7825         }
7826         ierr = PetscPrintf(PETSC_COMM_SELF, "\n");CHKERRQ(ierr);
7827         ierr = PetscPrintf(PETSC_COMM_SELF, "p: %D cone: ", support[s]);CHKERRQ(ierr);
7828         for (c = 0; c < coneSize; ++c) {
7829           ierr = PetscPrintf(PETSC_COMM_SELF, "%D, ", cone[c]);CHKERRQ(ierr);
7830         }
7831         ierr = PetscPrintf(PETSC_COMM_SELF, "\n");CHKERRQ(ierr);
7832         SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %D not found in cone of support point %D", p, support[s]);
7833       }
7834     }
7835   }
7836   if (storagecheck) {
7837     ierr = PetscSectionGetStorageSize(coneSection, &csize);CHKERRQ(ierr);
7838     ierr = PetscSectionGetStorageSize(supportSection, &ssize);CHKERRQ(ierr);
7839     if (csize != ssize) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %D != Total support size %D", csize, ssize);
7840   }
7841   PetscFunctionReturn(0);
7842 }
7843 
7844 /*
7845   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.
7846 */
7847 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
7848 {
7849   DMPolytopeType  cct;
7850   PetscInt        ptpoints[4];
7851   const PetscInt *cone, *ccone, *ptcone;
7852   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
7853   PetscErrorCode  ierr;
7854 
7855   PetscFunctionBegin;
7856   *unsplit = 0;
7857   switch (ct) {
7858     case DM_POLYTOPE_SEG_PRISM_TENSOR:
7859       ierr = DMPlexGetCone(dm, c, &cone);CHKERRQ(ierr);
7860       ierr = DMPlexGetConeSize(dm, c, &coneSize);CHKERRQ(ierr);
7861       for (cp = 0; cp < coneSize; ++cp) {
7862         ierr = DMPlexGetCellType(dm, cone[cp], &cct);CHKERRQ(ierr);
7863         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
7864       }
7865       break;
7866     case DM_POLYTOPE_TRI_PRISM_TENSOR:
7867     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
7868       ierr = DMPlexGetCone(dm, c, &cone);CHKERRQ(ierr);
7869       ierr = DMPlexGetConeSize(dm, c, &coneSize);CHKERRQ(ierr);
7870       for (cp = 0; cp < coneSize; ++cp) {
7871         ierr = DMPlexGetCone(dm, cone[cp], &ccone);CHKERRQ(ierr);
7872         ierr = DMPlexGetConeSize(dm, cone[cp], &cconeSize);CHKERRQ(ierr);
7873         for (ccp = 0; ccp < cconeSize; ++ccp) {
7874           ierr = DMPlexGetCellType(dm, ccone[ccp], &cct);CHKERRQ(ierr);
7875           if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
7876             PetscInt p;
7877             for (p = 0; p < npt; ++p) if (ptpoints[p] == ccone[ccp]) break;
7878             if (p == npt) ptpoints[npt++] = ccone[ccp];
7879           }
7880         }
7881       }
7882       break;
7883     default: break;
7884   }
7885   for (pt = 0; pt < npt; ++pt) {
7886     ierr = DMPlexGetCone(dm, ptpoints[pt], &ptcone);CHKERRQ(ierr);
7887     if (ptcone[0] == ptcone[1]) ++(*unsplit);
7888   }
7889   PetscFunctionReturn(0);
7890 }
7891 
7892 /*@
7893   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
7894 
7895   Input Parameters:
7896 + dm - The DMPlex object
7897 - cellHeight - Normally 0
7898 
7899   Notes:
7900   This is a useful diagnostic when creating meshes programmatically.
7901   Currently applicable only to homogeneous simplex or tensor meshes.
7902 
7903   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
7904 
7905   Level: developer
7906 
7907 .seealso: DMCreate(), DMSetFromOptions()
7908 @*/
7909 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
7910 {
7911   DMPlexInterpolatedFlag interp;
7912   DMPolytopeType         ct;
7913   PetscInt               vStart, vEnd, cStart, cEnd, c;
7914   PetscErrorCode         ierr;
7915 
7916   PetscFunctionBegin;
7917   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7918   ierr = DMPlexIsInterpolated(dm, &interp);CHKERRQ(ierr);
7919   ierr = DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd);CHKERRQ(ierr);
7920   ierr = DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd);CHKERRQ(ierr);
7921   for (c = cStart; c < cEnd; ++c) {
7922     PetscInt *closure = NULL;
7923     PetscInt  coneSize, closureSize, cl, Nv = 0;
7924 
7925     ierr = DMPlexGetCellType(dm, c, &ct);CHKERRQ(ierr);
7926     if ((PetscInt) ct < 0) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %D has no cell type", c);
7927     if (ct == DM_POLYTOPE_UNKNOWN) continue;
7928     if (interp == DMPLEX_INTERPOLATED_FULL) {
7929       ierr = DMPlexGetConeSize(dm, c, &coneSize);CHKERRQ(ierr);
7930       if (coneSize != DMPolytopeTypeGetConeSize(ct)) SETERRQ4(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %D of type %s has cone size %D != %D", c, DMPolytopeTypes[ct], coneSize, DMPolytopeTypeGetConeSize(ct));
7931     }
7932     ierr = DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure);CHKERRQ(ierr);
7933     for (cl = 0; cl < closureSize*2; cl += 2) {
7934       const PetscInt p = closure[cl];
7935       if ((p >= vStart) && (p < vEnd)) ++Nv;
7936     }
7937     ierr = DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure);CHKERRQ(ierr);
7938     /* Special Case: Tensor faces with identified vertices */
7939     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
7940       PetscInt unsplit;
7941 
7942       ierr = DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit);CHKERRQ(ierr);
7943       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
7944     }
7945     if (Nv != DMPolytopeTypeGetNumVertices(ct)) SETERRQ4(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %D of type %s has %D vertices != %D", c, DMPolytopeTypes[ct], Nv, DMPolytopeTypeGetNumVertices(ct));
7946   }
7947   PetscFunctionReturn(0);
7948 }
7949 
7950 /*@
7951   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
7952 
7953   Not Collective
7954 
7955   Input Parameters:
7956 + dm - The DMPlex object
7957 - cellHeight - Normally 0
7958 
7959   Notes:
7960   This is a useful diagnostic when creating meshes programmatically.
7961   This routine is only relevant for meshes that are fully interpolated across all ranks.
7962   It will error out if a partially interpolated mesh is given on some rank.
7963   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
7964 
7965   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
7966 
7967   Level: developer
7968 
7969 .seealso: DMCreate(), DMPlexGetVTKCellHeight(), DMSetFromOptions()
7970 @*/
7971 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
7972 {
7973   PetscInt       dim, depth, vStart, vEnd, cStart, cEnd, c, h;
7974   PetscErrorCode ierr;
7975   DMPlexInterpolatedFlag interpEnum;
7976 
7977   PetscFunctionBegin;
7978   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7979   ierr = DMPlexIsInterpolated(dm, &interpEnum);CHKERRQ(ierr);
7980   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(0);
7981   if (interpEnum == DMPLEX_INTERPOLATED_PARTIAL) {
7982     PetscMPIInt rank;
7983     MPI_Comm    comm;
7984 
7985     ierr = PetscObjectGetComm((PetscObject) dm, &comm);CHKERRQ(ierr);
7986     ierr = MPI_Comm_rank(comm, &rank);CHKERRMPI(ierr);
7987     SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_SUP, "Mesh is only partially interpolated on rank %d, this is currently not supported", rank);
7988   }
7989 
7990   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
7991   ierr = DMPlexGetDepth(dm, &depth);CHKERRQ(ierr);
7992   ierr = DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd);CHKERRQ(ierr);
7993   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
7994     ierr = DMPlexGetHeightStratum(dm, h, &cStart, &cEnd);CHKERRQ(ierr);
7995     for (c = cStart; c < cEnd; ++c) {
7996       const PetscInt      *cone, *ornt, *faceSizes, *faces;
7997       const DMPolytopeType *faceTypes;
7998       DMPolytopeType        ct;
7999       PetscInt              numFaces, coneSize, f;
8000       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
8001 
8002       ierr = DMPlexGetCellType(dm, c, &ct);CHKERRQ(ierr);
8003       ierr = DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit);CHKERRQ(ierr);
8004       if (unsplit) continue;
8005       ierr = DMPlexGetConeSize(dm, c, &coneSize);CHKERRQ(ierr);
8006       ierr = DMPlexGetCone(dm, c, &cone);CHKERRQ(ierr);
8007       ierr = DMPlexGetConeOrientation(dm, c, &ornt);CHKERRQ(ierr);
8008       ierr = DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure);CHKERRQ(ierr);
8009       for (cl = 0; cl < closureSize*2; cl += 2) {
8010         const PetscInt p = closure[cl];
8011         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
8012       }
8013       ierr = DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces);CHKERRQ(ierr);
8014       if (coneSize != numFaces) SETERRQ4(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %D of type %s has %D faces but should have %D", c, DMPolytopeTypes[ct], coneSize, numFaces);
8015       for (f = 0; f < numFaces; ++f) {
8016         DMPolytopeType fct;
8017         PetscInt       *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
8018 
8019         ierr = DMPlexGetCellType(dm, cone[f], &fct);CHKERRQ(ierr);
8020         ierr = DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure);CHKERRQ(ierr);
8021         for (cl = 0; cl < fclosureSize*2; cl += 2) {
8022           const PetscInt p = fclosure[cl];
8023           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
8024         }
8025         if (fnumCorners != faceSizes[f]) SETERRQ7(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %D of type %s (cone idx %D) of cell %D of type %s has %D vertices but should have %D", cone[f], DMPolytopeTypes[fct], f, c, DMPolytopeTypes[ct], fnumCorners, faceSizes[f]);
8026         for (v = 0; v < fnumCorners; ++v) {
8027           if (fclosure[v] != faces[fOff+v]) SETERRQ8(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %D of type %s (cone idx %d) of cell %D of type %s vertex %D, %D != %D", cone[f], DMPolytopeTypes[fct], f, c, DMPolytopeTypes[ct], v, fclosure[v], faces[fOff+v]);
8028         }
8029         ierr = DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure);CHKERRQ(ierr);
8030         fOff += faceSizes[f];
8031       }
8032       ierr = DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces);CHKERRQ(ierr);
8033       ierr = DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure);CHKERRQ(ierr);
8034     }
8035   }
8036   PetscFunctionReturn(0);
8037 }
8038 
8039 /*@
8040   DMPlexCheckGeometry - Check the geometry of mesh cells
8041 
8042   Input Parameter:
8043 . dm - The DMPlex object
8044 
8045   Notes:
8046   This is a useful diagnostic when creating meshes programmatically.
8047 
8048   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8049 
8050   Level: developer
8051 
8052 .seealso: DMCreate(), DMSetFromOptions()
8053 @*/
8054 PetscErrorCode DMPlexCheckGeometry(DM dm)
8055 {
8056   Vec            coordinates;
8057   PetscReal      detJ, J[9], refVol = 1.0;
8058   PetscReal      vol;
8059   PetscBool      periodic;
8060   PetscInt       dim, depth, dE, d, cStart, cEnd, c;
8061   PetscErrorCode ierr;
8062 
8063   PetscFunctionBegin;
8064   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
8065   ierr = DMGetCoordinateDim(dm, &dE);CHKERRQ(ierr);
8066   if (dim != dE) PetscFunctionReturn(0);
8067   ierr = DMPlexGetDepth(dm, &depth);CHKERRQ(ierr);
8068   ierr = DMGetPeriodicity(dm, &periodic, NULL, NULL, NULL);CHKERRQ(ierr);
8069   for (d = 0; d < dim; ++d) refVol *= 2.0;
8070   ierr = DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd);CHKERRQ(ierr);
8071   /* Make sure local coordinates are created, because that step is collective */
8072   ierr = DMGetCoordinatesLocal(dm, &coordinates);CHKERRQ(ierr);
8073   for (c = cStart; c < cEnd; ++c) {
8074     DMPolytopeType ct;
8075     PetscInt       unsplit;
8076     PetscBool      ignoreZeroVol = PETSC_FALSE;
8077 
8078     ierr = DMPlexGetCellType(dm, c, &ct);CHKERRQ(ierr);
8079     switch (ct) {
8080       case DM_POLYTOPE_SEG_PRISM_TENSOR:
8081       case DM_POLYTOPE_TRI_PRISM_TENSOR:
8082       case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8083         ignoreZeroVol = PETSC_TRUE; break;
8084       default: break;
8085     }
8086     switch (ct) {
8087       case DM_POLYTOPE_TRI_PRISM:
8088       case DM_POLYTOPE_TRI_PRISM_TENSOR:
8089       case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8090       case DM_POLYTOPE_PYRAMID:
8091         continue;
8092       default: break;
8093     }
8094     ierr = DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit);CHKERRQ(ierr);
8095     if (unsplit) continue;
8096     ierr = DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ);CHKERRQ(ierr);
8097     if (detJ < -PETSC_SMALL || (detJ <= 0.0 && !ignoreZeroVol)) SETERRQ3(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %D of type %s is inverted, |J| = %g", c, DMPolytopeTypes[ct], (double) detJ);
8098     ierr = PetscInfo2(dm, "Cell %D FEM Volume %g\n", c, (double) detJ*refVol);CHKERRQ(ierr);
8099     if (depth > 1 && !periodic) {
8100       ierr = DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL);CHKERRQ(ierr);
8101       if (vol < -PETSC_SMALL || (vol <= 0.0 && !ignoreZeroVol)) SETERRQ3(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %D of type %s is inverted, vol = %g", c, DMPolytopeTypes[ct], (double) vol);
8102       ierr = PetscInfo2(dm, "Cell %D FVM Volume %g\n", c, (double) vol);CHKERRQ(ierr);
8103     }
8104   }
8105   PetscFunctionReturn(0);
8106 }
8107 
8108 /*@
8109   DMPlexCheckPointSF - Check that several necessary conditions are met for the point SF of this plex.
8110 
8111   Input Parameters:
8112 . dm - The DMPlex object
8113 
8114   Notes:
8115   This is mainly intended for debugging/testing purposes.
8116   It currently checks only meshes with no partition overlapping.
8117 
8118   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8119 
8120   Level: developer
8121 
8122 .seealso: DMGetPointSF(), DMSetFromOptions()
8123 @*/
8124 PetscErrorCode DMPlexCheckPointSF(DM dm)
8125 {
8126   PetscSF         pointSF;
8127   PetscInt        cellHeight, cStart, cEnd, l, nleaves, nroots, overlap;
8128   const PetscInt *locals, *rootdegree;
8129   PetscBool       distributed;
8130   PetscErrorCode  ierr;
8131 
8132   PetscFunctionBegin;
8133   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8134   ierr = DMGetPointSF(dm, &pointSF);CHKERRQ(ierr);
8135   ierr = DMPlexIsDistributed(dm, &distributed);CHKERRQ(ierr);
8136   if (!distributed) PetscFunctionReturn(0);
8137   ierr = DMPlexGetOverlap(dm, &overlap);CHKERRQ(ierr);
8138   if (overlap) {
8139     ierr = PetscPrintf(PetscObjectComm((PetscObject)dm), "Warning: DMPlexCheckPointSF() is currently not implemented for meshes with partition overlapping");
8140     PetscFunctionReturn(0);
8141   }
8142   if (!pointSF) SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "This DMPlex is distributed but does not have PointSF attached");
8143   ierr = PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, NULL);CHKERRQ(ierr);
8144   if (nroots < 0) SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "This DMPlex is distributed but its PointSF has no graph set");
8145   ierr = PetscSFComputeDegreeBegin(pointSF, &rootdegree);CHKERRQ(ierr);
8146   ierr = PetscSFComputeDegreeEnd(pointSF, &rootdegree);CHKERRQ(ierr);
8147 
8148   /* 1) check there are no faces in 2D, cells in 3D, in interface */
8149   ierr = DMPlexGetVTKCellHeight(dm, &cellHeight);CHKERRQ(ierr);
8150   ierr = DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd);CHKERRQ(ierr);
8151   for (l = 0; l < nleaves; ++l) {
8152     const PetscInt point = locals[l];
8153 
8154     if (point >= cStart && point < cEnd) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %D which is a cell", point);
8155   }
8156 
8157   /* 2) if some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
8158   for (l = 0; l < nleaves; ++l) {
8159     const PetscInt  point = locals[l];
8160     const PetscInt *cone;
8161     PetscInt        coneSize, c, idx;
8162 
8163     ierr = DMPlexGetConeSize(dm, point, &coneSize);CHKERRQ(ierr);
8164     ierr = DMPlexGetCone(dm, point, &cone);CHKERRQ(ierr);
8165     for (c = 0; c < coneSize; ++c) {
8166       if (!rootdegree[cone[c]]) {
8167         ierr = PetscFindInt(cone[c], nleaves, locals, &idx);CHKERRQ(ierr);
8168         if (idx < 0) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %D but not %D from its cone", point, cone[c]);
8169       }
8170     }
8171   }
8172   PetscFunctionReturn(0);
8173 }
8174 
8175 typedef struct cell_stats
8176 {
8177   PetscReal min, max, sum, squaresum;
8178   PetscInt  count;
8179 } cell_stats_t;
8180 
8181 static void MPIAPI cell_stats_reduce(void *a, void *b, int * len, MPI_Datatype *datatype)
8182 {
8183   PetscInt i, N = *len;
8184 
8185   for (i = 0; i < N; i++) {
8186     cell_stats_t *A = (cell_stats_t *) a;
8187     cell_stats_t *B = (cell_stats_t *) b;
8188 
8189     B->min = PetscMin(A->min,B->min);
8190     B->max = PetscMax(A->max,B->max);
8191     B->sum += A->sum;
8192     B->squaresum += A->squaresum;
8193     B->count += A->count;
8194   }
8195 }
8196 
8197 /*@
8198   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
8199 
8200   Collective on dm
8201 
8202   Input Parameters:
8203 + dm        - The DMPlex object
8204 . output    - If true, statistics will be displayed on stdout
8205 - condLimit - Display all cells above this condition number, or PETSC_DETERMINE for no cell output
8206 
8207   Notes:
8208   This is mainly intended for debugging/testing purposes.
8209 
8210   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8211 
8212   Level: developer
8213 
8214 .seealso: DMSetFromOptions(), DMPlexComputeOrthogonalQuality()
8215 @*/
8216 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
8217 {
8218   DM             dmCoarse;
8219   cell_stats_t   stats, globalStats;
8220   MPI_Comm       comm = PetscObjectComm((PetscObject)dm);
8221   PetscReal      *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
8222   PetscReal      limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
8223   PetscInt       cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
8224   PetscMPIInt    rank,size;
8225   PetscErrorCode ierr;
8226 
8227   PetscFunctionBegin;
8228   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8229   stats.min   = PETSC_MAX_REAL;
8230   stats.max   = PETSC_MIN_REAL;
8231   stats.sum   = stats.squaresum = 0.;
8232   stats.count = 0;
8233 
8234   ierr = MPI_Comm_size(comm, &size);CHKERRMPI(ierr);
8235   ierr = MPI_Comm_rank(comm, &rank);CHKERRMPI(ierr);
8236   ierr = DMGetCoordinateDim(dm,&cdim);CHKERRQ(ierr);
8237   ierr = PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ);CHKERRQ(ierr);
8238   ierr = DMPlexGetSimplexOrBoxCells(dm,0,&cStart,&cEnd);CHKERRQ(ierr);
8239   ierr = DMPlexGetDepthStratum(dm,1,&eStart,&eEnd);CHKERRQ(ierr);
8240   for (c = cStart; c < cEnd; c++) {
8241     PetscInt  i;
8242     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
8243 
8244     ierr = DMPlexComputeCellGeometryAffineFEM(dm,c,NULL,J,invJ,&detJ);CHKERRQ(ierr);
8245     if (detJ < 0.0) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %D is inverted", c);
8246     for (i = 0; i < PetscSqr(cdim); ++i) {
8247       frobJ    += J[i] * J[i];
8248       frobInvJ += invJ[i] * invJ[i];
8249     }
8250     cond2 = frobJ * frobInvJ;
8251     cond  = PetscSqrtReal(cond2);
8252 
8253     stats.min        = PetscMin(stats.min,cond);
8254     stats.max        = PetscMax(stats.max,cond);
8255     stats.sum       += cond;
8256     stats.squaresum += cond2;
8257     stats.count++;
8258     if (output && cond > limit) {
8259       PetscSection coordSection;
8260       Vec          coordsLocal;
8261       PetscScalar *coords = NULL;
8262       PetscInt     Nv, d, clSize, cl, *closure = NULL;
8263 
8264       ierr = DMGetCoordinatesLocal(dm, &coordsLocal);CHKERRQ(ierr);
8265       ierr = DMGetCoordinateSection(dm, &coordSection);CHKERRQ(ierr);
8266       ierr = DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords);CHKERRQ(ierr);
8267       ierr = PetscSynchronizedPrintf(comm, "[%d] Cell %D cond %g\n", rank, c, (double) cond);CHKERRQ(ierr);
8268       for (i = 0; i < Nv/cdim; ++i) {
8269         ierr = PetscSynchronizedPrintf(comm, "  Vertex %D: (", i);CHKERRQ(ierr);
8270         for (d = 0; d < cdim; ++d) {
8271           if (d > 0) {ierr = PetscSynchronizedPrintf(comm, ", ");CHKERRQ(ierr);}
8272           ierr = PetscSynchronizedPrintf(comm, "%g", (double) PetscRealPart(coords[i*cdim+d]));CHKERRQ(ierr);
8273         }
8274         ierr = PetscSynchronizedPrintf(comm, ")\n");CHKERRQ(ierr);
8275       }
8276       ierr = DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure);CHKERRQ(ierr);
8277       for (cl = 0; cl < clSize*2; cl += 2) {
8278         const PetscInt edge = closure[cl];
8279 
8280         if ((edge >= eStart) && (edge < eEnd)) {
8281           PetscReal len;
8282 
8283           ierr = DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL);CHKERRQ(ierr);
8284           ierr = PetscSynchronizedPrintf(comm, "  Edge %D: length %g\n", edge, (double) len);CHKERRQ(ierr);
8285         }
8286       }
8287       ierr = DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure);CHKERRQ(ierr);
8288       ierr = DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords);CHKERRQ(ierr);
8289     }
8290   }
8291   if (output) {ierr = PetscSynchronizedFlush(comm, NULL);CHKERRQ(ierr);}
8292 
8293   if (size > 1) {
8294     PetscMPIInt   blockLengths[2] = {4,1};
8295     MPI_Aint      blockOffsets[2] = {offsetof(cell_stats_t,min),offsetof(cell_stats_t,count)};
8296     MPI_Datatype  blockTypes[2]   = {MPIU_REAL,MPIU_INT}, statType;
8297     MPI_Op        statReduce;
8298 
8299     ierr = MPI_Type_create_struct(2,blockLengths,blockOffsets,blockTypes,&statType);CHKERRMPI(ierr);
8300     ierr = MPI_Type_commit(&statType);CHKERRMPI(ierr);
8301     ierr = MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce);CHKERRMPI(ierr);
8302     ierr = MPI_Reduce(&stats,&globalStats,1,statType,statReduce,0,comm);CHKERRMPI(ierr);
8303     ierr = MPI_Op_free(&statReduce);CHKERRMPI(ierr);
8304     ierr = MPI_Type_free(&statType);CHKERRMPI(ierr);
8305   } else {
8306     ierr = PetscArraycpy(&globalStats,&stats,1);CHKERRQ(ierr);
8307   }
8308   if (!rank) {
8309     count = globalStats.count;
8310     min   = globalStats.min;
8311     max   = globalStats.max;
8312     mean  = globalStats.sum / globalStats.count;
8313     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1),0)) : 0.0;
8314   }
8315 
8316   if (output) {
8317     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);
8318   }
8319   ierr = PetscFree2(J,invJ);CHKERRQ(ierr);
8320 
8321   ierr = DMGetCoarseDM(dm,&dmCoarse);CHKERRQ(ierr);
8322   if (dmCoarse) {
8323     PetscBool isplex;
8324 
8325     ierr = PetscObjectTypeCompare((PetscObject)dmCoarse,DMPLEX,&isplex);CHKERRQ(ierr);
8326     if (isplex) {
8327       ierr = DMPlexCheckCellShape(dmCoarse,output,condLimit);CHKERRQ(ierr);
8328     }
8329   }
8330   PetscFunctionReturn(0);
8331 }
8332 
8333 /*@
8334   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
8335   orthogonal quality below given tolerance.
8336 
8337   Collective on dm
8338 
8339   Input Parameters:
8340 + dm   - The DMPlex object
8341 . fv   - Optional PetscFV object for pre-computed cell/face centroid information
8342 - atol - [0, 1] Absolute tolerance for tagging cells.
8343 
8344   Output Parameters:
8345 + OrthQual      - Vec containing orthogonal quality per cell
8346 - OrthQualLabel - DMLabel tagging cells below atol with DM_ADAPT_REFINE
8347 
8348   Options Database Keys:
8349 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only PETSCVIEWERASCII is
8350 supported.
8351 - -dm_plex_orthogonal_quality_vec_view - view OrthQual vector.
8352 
8353   Notes:
8354   Orthogonal quality is given by the following formula:
8355 
8356   \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]
8357 
8358   Where A_i is the i'th face-normal vector, f_i is the vector from the cell centroid to the i'th face centroid, and c_i
8359   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
8360   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
8361   calculating the cosine of the angle between these vectors.
8362 
8363   Orthogonal quality ranges from 1 (best) to 0 (worst).
8364 
8365   This routine is mainly useful for FVM, however is not restricted to only FVM. The PetscFV object is optionally used to check for
8366   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
8367 
8368   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
8369 
8370   Level: intermediate
8371 
8372 .seealso: DMPlexCheckCellShape(), DMCreateLabel()
8373 @*/
8374 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
8375 {
8376   PetscInt                nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
8377   PetscInt                *idx;
8378   PetscScalar             *oqVals;
8379   const PetscScalar       *cellGeomArr, *faceGeomArr;
8380   PetscReal               *ci, *fi, *Ai;
8381   MPI_Comm                comm;
8382   Vec                     cellgeom, facegeom;
8383   DM                      dmFace, dmCell;
8384   IS                      glob;
8385   ISLocalToGlobalMapping  ltog;
8386   PetscViewer             vwr;
8387   PetscErrorCode          ierr;
8388 
8389   PetscFunctionBegin;
8390   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8391   if (fv) {PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);}
8392   PetscValidPointer(OrthQual, 4);
8393   if (PetscUnlikelyDebug(atol < 0.0 || atol > 1.0)) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Absolute tolerance %g not in [0,1]",(double)atol);
8394   ierr = PetscObjectGetComm((PetscObject) dm, &comm);CHKERRQ(ierr);
8395   ierr = DMGetDimension(dm, &nc);CHKERRQ(ierr);
8396   if (nc < 2) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %D)", nc);
8397   {
8398     DMPlexInterpolatedFlag interpFlag;
8399 
8400     ierr = DMPlexIsInterpolated(dm, &interpFlag);CHKERRQ(ierr);
8401     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
8402       PetscMPIInt rank;
8403 
8404       ierr = MPI_Comm_rank(comm, &rank);CHKERRMPI(ierr);
8405       SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
8406     }
8407   }
8408   if (OrthQualLabel) {
8409     PetscValidPointer(OrthQualLabel, 5);
8410     ierr = DMCreateLabel(dm, "Orthogonal_Quality");CHKERRQ(ierr);
8411     ierr = DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel);CHKERRQ(ierr);
8412   } else {*OrthQualLabel = NULL;}
8413   ierr = DMPlexGetVTKCellHeight(dm, &cellHeight);CHKERRQ(ierr);
8414   ierr = DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd);CHKERRQ(ierr);
8415   ierr = DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob);CHKERRQ(ierr);
8416   ierr = ISLocalToGlobalMappingCreateIS(glob, &ltog);CHKERRQ(ierr);
8417   ierr = ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH);CHKERRQ(ierr);
8418   ierr = VecCreate(comm, OrthQual);CHKERRQ(ierr);
8419   ierr = VecSetType(*OrthQual, VECSTANDARD);CHKERRQ(ierr);
8420   ierr = VecSetSizes(*OrthQual, cEnd-cStart, PETSC_DETERMINE);CHKERRQ(ierr);
8421   ierr = VecSetLocalToGlobalMapping(*OrthQual, ltog);CHKERRQ(ierr);
8422   ierr = VecSetUp(*OrthQual);CHKERRQ(ierr);
8423   ierr = ISDestroy(&glob);CHKERRQ(ierr);
8424   ierr = ISLocalToGlobalMappingDestroy(&ltog);CHKERRQ(ierr);
8425   ierr = DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL);CHKERRQ(ierr);
8426   ierr = VecGetArrayRead(cellgeom, &cellGeomArr);CHKERRQ(ierr);
8427   ierr = VecGetArrayRead(facegeom, &faceGeomArr);CHKERRQ(ierr);
8428   ierr = VecGetDM(cellgeom, &dmCell);CHKERRQ(ierr);
8429   ierr = VecGetDM(facegeom, &dmFace);CHKERRQ(ierr);
8430   ierr = PetscMalloc5(cEnd-cStart, &idx, cEnd-cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai);CHKERRQ(ierr);
8431   for (cell = cStart; cell < cEnd; cellIter++,cell++) {
8432     PetscInt           cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
8433     PetscInt           cellarr[2], *adj = NULL;
8434     PetscScalar        *cArr, *fArr;
8435     PetscReal          minvalc = 1.0, minvalf = 1.0;
8436     PetscFVCellGeom    *cg;
8437 
8438     idx[cellIter] = cell-cStart;
8439     cellarr[0] = cell;
8440     /* Make indexing into cellGeom easier */
8441     ierr = DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg);CHKERRQ(ierr);
8442     ierr = DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj);CHKERRQ(ierr);
8443     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
8444     ierr = PetscCalloc2(adjSize, &cArr, adjSize, &fArr);CHKERRQ(ierr);
8445     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++,cellneigh++) {
8446       PetscInt         i;
8447       const PetscInt   neigh = adj[cellneigh];
8448       PetscReal        normci = 0, normfi = 0, normai = 0;
8449       PetscFVCellGeom  *cgneigh;
8450       PetscFVFaceGeom  *fg;
8451 
8452       /* Don't count ourselves in the neighbor list */
8453       if (neigh == cell) continue;
8454       ierr = DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh);CHKERRQ(ierr);
8455       cellarr[1] = neigh;
8456       {
8457         PetscInt       numcovpts;
8458         const PetscInt *covpts;
8459 
8460         ierr = DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts);CHKERRQ(ierr);
8461         ierr = DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg);CHKERRQ(ierr);
8462         ierr = DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts);CHKERRQ(ierr);
8463       }
8464 
8465       /* Compute c_i, f_i and their norms */
8466       for (i = 0; i < nc; i++) {
8467         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
8468         fi[i] = fg->centroid[i] - cg->centroid[i];
8469         Ai[i] = fg->normal[i];
8470         normci += PetscPowReal(ci[i], 2);
8471         normfi += PetscPowReal(fi[i], 2);
8472         normai += PetscPowReal(Ai[i], 2);
8473       }
8474       normci = PetscSqrtReal(normci);
8475       normfi = PetscSqrtReal(normfi);
8476       normai = PetscSqrtReal(normai);
8477 
8478       /* Normalize and compute for each face-cell-normal pair */
8479       for (i = 0; i < nc; i++) {
8480         ci[i] = ci[i]/normci;
8481         fi[i] = fi[i]/normfi;
8482         Ai[i] = Ai[i]/normai;
8483         /* PetscAbs because I don't know if normals are guaranteed to point out */
8484         cArr[cellneighiter] += PetscAbs(Ai[i]*ci[i]);
8485         fArr[cellneighiter] += PetscAbs(Ai[i]*fi[i]);
8486       }
8487       if (PetscRealPart(cArr[cellneighiter]) < minvalc) {
8488         minvalc = PetscRealPart(cArr[cellneighiter]);
8489       }
8490       if (PetscRealPart(fArr[cellneighiter]) < minvalf) {
8491         minvalf = PetscRealPart(fArr[cellneighiter]);
8492       }
8493     }
8494     ierr = PetscFree(adj);CHKERRQ(ierr);
8495     ierr = PetscFree2(cArr, fArr);CHKERRQ(ierr);
8496     /* Defer to cell if they're equal */
8497     oqVals[cellIter] = PetscMin(minvalf, minvalc);
8498     if (OrthQualLabel) {
8499       if (PetscRealPart(oqVals[cellIter]) <= atol) {ierr = DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE);CHKERRQ(ierr);}
8500     }
8501   }
8502   ierr = VecSetValuesLocal(*OrthQual, cEnd-cStart, idx, oqVals, INSERT_VALUES);CHKERRQ(ierr);
8503   ierr = VecAssemblyBegin(*OrthQual);CHKERRQ(ierr);
8504   ierr = VecAssemblyEnd(*OrthQual);CHKERRQ(ierr);
8505   ierr = VecRestoreArrayRead(cellgeom, &cellGeomArr);CHKERRQ(ierr);
8506   ierr = VecRestoreArrayRead(facegeom, &faceGeomArr);CHKERRQ(ierr);
8507   ierr = PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL);CHKERRQ(ierr);
8508   if (OrthQualLabel) {
8509     if (vwr) {ierr = DMLabelView(*OrthQualLabel, vwr);CHKERRQ(ierr);}
8510   }
8511   ierr = PetscFree5(idx, oqVals, ci, fi, Ai);CHKERRQ(ierr);
8512   ierr = PetscViewerDestroy(&vwr);CHKERRQ(ierr);
8513   ierr = VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view");CHKERRQ(ierr);
8514   PetscFunctionReturn(0);
8515 }
8516 
8517 /* this is here insead of DMGetOutputDM because output DM still has constraints in the local indices that affect
8518  * interpolator construction */
8519 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
8520 {
8521   PetscSection   section, newSection, gsection;
8522   PetscSF        sf;
8523   PetscBool      hasConstraints, ghasConstraints;
8524   PetscErrorCode ierr;
8525 
8526   PetscFunctionBegin;
8527   PetscValidHeaderSpecific(dm,DM_CLASSID,1);
8528   PetscValidPointer(odm,2);
8529   ierr = DMGetLocalSection(dm, &section);CHKERRQ(ierr);
8530   ierr = PetscSectionHasConstraints(section, &hasConstraints);CHKERRQ(ierr);
8531   ierr = MPI_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject) dm));CHKERRMPI(ierr);
8532   if (!ghasConstraints) {
8533     ierr = PetscObjectReference((PetscObject)dm);CHKERRQ(ierr);
8534     *odm = dm;
8535     PetscFunctionReturn(0);
8536   }
8537   ierr = DMClone(dm, odm);CHKERRQ(ierr);
8538   ierr = DMCopyFields(dm, *odm);CHKERRQ(ierr);
8539   ierr = DMGetLocalSection(*odm, &newSection);CHKERRQ(ierr);
8540   ierr = DMGetPointSF(*odm, &sf);CHKERRQ(ierr);
8541   ierr = PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection);CHKERRQ(ierr);
8542   ierr = DMSetGlobalSection(*odm, gsection);CHKERRQ(ierr);
8543   ierr = PetscSectionDestroy(&gsection);CHKERRQ(ierr);
8544   PetscFunctionReturn(0);
8545 }
8546 
8547 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
8548 {
8549   DM             dmco, dmfo;
8550   Mat            interpo;
8551   Vec            rscale;
8552   Vec            cglobalo, clocal;
8553   Vec            fglobal, fglobalo, flocal;
8554   PetscBool      regular;
8555   PetscErrorCode ierr;
8556 
8557   PetscFunctionBegin;
8558   ierr = DMGetFullDM(dmc, &dmco);CHKERRQ(ierr);
8559   ierr = DMGetFullDM(dmf, &dmfo);CHKERRQ(ierr);
8560   ierr = DMSetCoarseDM(dmfo, dmco);CHKERRQ(ierr);
8561   ierr = DMPlexGetRegularRefinement(dmf, &regular);CHKERRQ(ierr);
8562   ierr = DMPlexSetRegularRefinement(dmfo, regular);CHKERRQ(ierr);
8563   ierr = DMCreateInterpolation(dmco, dmfo, &interpo, &rscale);CHKERRQ(ierr);
8564   ierr = DMCreateGlobalVector(dmco, &cglobalo);CHKERRQ(ierr);
8565   ierr = DMCreateLocalVector(dmc, &clocal);CHKERRQ(ierr);
8566   ierr = VecSet(cglobalo, 0.);CHKERRQ(ierr);
8567   ierr = VecSet(clocal, 0.);CHKERRQ(ierr);
8568   ierr = DMCreateGlobalVector(dmf, &fglobal);CHKERRQ(ierr);
8569   ierr = DMCreateGlobalVector(dmfo, &fglobalo);CHKERRQ(ierr);
8570   ierr = DMCreateLocalVector(dmf, &flocal);CHKERRQ(ierr);
8571   ierr = VecSet(fglobal, 0.);CHKERRQ(ierr);
8572   ierr = VecSet(fglobalo, 0.);CHKERRQ(ierr);
8573   ierr = VecSet(flocal, 0.);CHKERRQ(ierr);
8574   ierr = DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL);CHKERRQ(ierr);
8575   ierr = DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo);CHKERRQ(ierr);
8576   ierr = DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo);CHKERRQ(ierr);
8577   ierr = MatMult(interpo, cglobalo, fglobalo);CHKERRQ(ierr);
8578   ierr = DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal);CHKERRQ(ierr);
8579   ierr = DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal);CHKERRQ(ierr);
8580   ierr = DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal);CHKERRQ(ierr);
8581   ierr = DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal);CHKERRQ(ierr);
8582   *shift = fglobal;
8583   ierr = VecDestroy(&flocal);CHKERRQ(ierr);
8584   ierr = VecDestroy(&fglobalo);CHKERRQ(ierr);
8585   ierr = VecDestroy(&clocal);CHKERRQ(ierr);
8586   ierr = VecDestroy(&cglobalo);CHKERRQ(ierr);
8587   ierr = VecDestroy(&rscale);CHKERRQ(ierr);
8588   ierr = MatDestroy(&interpo);CHKERRQ(ierr);
8589   ierr = DMDestroy(&dmfo);CHKERRQ(ierr);
8590   ierr = DMDestroy(&dmco);CHKERRQ(ierr);
8591   PetscFunctionReturn(0);
8592 }
8593 
8594 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
8595 {
8596   PetscObject    shifto;
8597   Vec            shift;
8598 
8599   PetscErrorCode ierr;
8600 
8601   PetscFunctionBegin;
8602   if (!interp) {
8603     Vec rscale;
8604 
8605     ierr = DMCreateInterpolation(coarse, fine, &interp, &rscale);CHKERRQ(ierr);
8606     ierr = VecDestroy(&rscale);CHKERRQ(ierr);
8607   } else {
8608     ierr = PetscObjectReference((PetscObject)interp);CHKERRQ(ierr);
8609   }
8610   ierr = PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto);CHKERRQ(ierr);
8611   if (!shifto) {
8612     ierr = DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift);CHKERRQ(ierr);
8613     ierr = PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject) shift);CHKERRQ(ierr);
8614     shifto = (PetscObject) shift;
8615     ierr = VecDestroy(&shift);CHKERRQ(ierr);
8616   }
8617   shift = (Vec) shifto;
8618   ierr = MatInterpolate(interp, coarseSol, fineSol);CHKERRQ(ierr);
8619   ierr = VecAXPY(fineSol, 1.0, shift);CHKERRQ(ierr);
8620   ierr = MatDestroy(&interp);CHKERRQ(ierr);
8621   PetscFunctionReturn(0);
8622 }
8623 
8624 /* Pointwise interpolation
8625      Just code FEM for now
8626      u^f = I u^c
8627      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
8628      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
8629      I_{ij} = psi^f_i phi^c_j
8630 */
8631 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
8632 {
8633   PetscSection   gsc, gsf;
8634   PetscInt       m, n;
8635   void          *ctx;
8636   DM             cdm;
8637   PetscBool      regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
8638   PetscErrorCode ierr;
8639 
8640   PetscFunctionBegin;
8641   ierr = DMGetGlobalSection(dmFine, &gsf);CHKERRQ(ierr);
8642   ierr = PetscSectionGetConstrainedStorageSize(gsf, &m);CHKERRQ(ierr);
8643   ierr = DMGetGlobalSection(dmCoarse, &gsc);CHKERRQ(ierr);
8644   ierr = PetscSectionGetConstrainedStorageSize(gsc, &n);CHKERRQ(ierr);
8645 
8646   ierr = PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis);CHKERRQ(ierr);
8647   ierr = MatCreate(PetscObjectComm((PetscObject) dmCoarse), interpolation);CHKERRQ(ierr);
8648   ierr = MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE);CHKERRQ(ierr);
8649   ierr = MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype);CHKERRQ(ierr);
8650   ierr = DMGetApplicationContext(dmFine, &ctx);CHKERRQ(ierr);
8651 
8652   ierr = DMGetCoarseDM(dmFine, &cdm);CHKERRQ(ierr);
8653   ierr = DMPlexGetRegularRefinement(dmFine, &regular);CHKERRQ(ierr);
8654   if (!isRefined || (regular && cdm == dmCoarse)) {ierr = DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx);CHKERRQ(ierr);}
8655   else                                            {ierr = DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx);CHKERRQ(ierr);}
8656   ierr = MatViewFromOptions(*interpolation, NULL, "-interp_mat_view");CHKERRQ(ierr);
8657   if (scaling) {
8658     /* Use naive scaling */
8659     ierr = DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling);CHKERRQ(ierr);
8660   }
8661   PetscFunctionReturn(0);
8662 }
8663 
8664 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
8665 {
8666   PetscErrorCode ierr;
8667   VecScatter     ctx;
8668 
8669   PetscFunctionBegin;
8670   ierr = DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL);CHKERRQ(ierr);
8671   ierr = MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat);CHKERRQ(ierr);
8672   ierr = VecScatterDestroy(&ctx);CHKERRQ(ierr);
8673   PetscFunctionReturn(0);
8674 }
8675 
8676 static void g0_identity_private(PetscInt dim, PetscInt Nf, PetscInt NfAux,
8677                                 const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[],
8678                                 const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[],
8679                                 PetscReal t, PetscReal u_tShift, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar g0[])
8680 {
8681   g0[0] = 1.0;
8682 }
8683 
8684 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
8685 {
8686   PetscSection   gsc, gsf;
8687   PetscInt       m, n;
8688   void          *ctx;
8689   DM             cdm;
8690   PetscBool      regular;
8691   PetscErrorCode ierr;
8692 
8693   PetscFunctionBegin;
8694   if (dmFine == dmCoarse) {
8695     DM       dmc;
8696     PetscDS  ds;
8697     Vec      u;
8698     IS       cellIS;
8699     PetscFormKey key;
8700     PetscInt depth;
8701 
8702     ierr = DMClone(dmFine, &dmc);CHKERRQ(ierr);
8703     ierr = DMCopyDisc(dmFine, dmc);CHKERRQ(ierr);
8704     ierr = DMGetDS(dmc, &ds);CHKERRQ(ierr);
8705     ierr = PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL);CHKERRQ(ierr);
8706     ierr = DMCreateMatrix(dmc, mass);CHKERRQ(ierr);
8707     ierr = DMGetGlobalVector(dmc, &u);CHKERRQ(ierr);
8708     ierr = DMPlexGetDepth(dmc, &depth);CHKERRQ(ierr);
8709     ierr = DMGetStratumIS(dmc, "depth", depth, &cellIS);CHKERRQ(ierr);
8710     ierr = MatZeroEntries(*mass);CHKERRQ(ierr);
8711     key.label = NULL;
8712     key.value = 0;
8713     key.field = 0;
8714     key.part  = 0;
8715     ierr = DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL);CHKERRQ(ierr);
8716     ierr = ISDestroy(&cellIS);CHKERRQ(ierr);
8717     ierr = DMRestoreGlobalVector(dmc, &u);CHKERRQ(ierr);
8718     ierr = DMDestroy(&dmc);CHKERRQ(ierr);
8719   } else {
8720     ierr = DMGetGlobalSection(dmFine, &gsf);CHKERRQ(ierr);
8721     ierr = PetscSectionGetConstrainedStorageSize(gsf, &m);CHKERRQ(ierr);
8722     ierr = DMGetGlobalSection(dmCoarse, &gsc);CHKERRQ(ierr);
8723     ierr = PetscSectionGetConstrainedStorageSize(gsc, &n);CHKERRQ(ierr);
8724 
8725     ierr = MatCreate(PetscObjectComm((PetscObject) dmCoarse), mass);CHKERRQ(ierr);
8726     ierr = MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE);CHKERRQ(ierr);
8727     ierr = MatSetType(*mass, dmCoarse->mattype);CHKERRQ(ierr);
8728     ierr = DMGetApplicationContext(dmFine, &ctx);CHKERRQ(ierr);
8729 
8730     ierr = DMGetCoarseDM(dmFine, &cdm);CHKERRQ(ierr);
8731     ierr = DMPlexGetRegularRefinement(dmFine, &regular);CHKERRQ(ierr);
8732     if (regular && cdm == dmCoarse) {ierr = DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx);CHKERRQ(ierr);}
8733     else                            {ierr = DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx);CHKERRQ(ierr);}
8734   }
8735   ierr = MatViewFromOptions(*mass, NULL, "-mass_mat_view");CHKERRQ(ierr);
8736   PetscFunctionReturn(0);
8737 }
8738 
8739 /*@
8740   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
8741 
8742   Input Parameter:
8743 . dm - The DMPlex object
8744 
8745   Output Parameter:
8746 . regular - The flag
8747 
8748   Level: intermediate
8749 
8750 .seealso: DMPlexSetRegularRefinement()
8751 @*/
8752 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
8753 {
8754   PetscFunctionBegin;
8755   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8756   PetscValidPointer(regular, 2);
8757   *regular = ((DM_Plex *) dm->data)->regularRefinement;
8758   PetscFunctionReturn(0);
8759 }
8760 
8761 /*@
8762   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
8763 
8764   Input Parameters:
8765 + dm - The DMPlex object
8766 - regular - The flag
8767 
8768   Level: intermediate
8769 
8770 .seealso: DMPlexGetRegularRefinement()
8771 @*/
8772 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
8773 {
8774   PetscFunctionBegin;
8775   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8776   ((DM_Plex *) dm->data)->regularRefinement = regular;
8777   PetscFunctionReturn(0);
8778 }
8779 
8780 /*@
8781   DMPlexGetCellRefinerType - Get the strategy for refining a cell
8782 
8783   Input Parameter:
8784 . dm - The DMPlex object
8785 
8786   Output Parameter:
8787 . cr - The strategy number
8788 
8789   Level: intermediate
8790 
8791 .seealso: DMPlexSetCellRefinerType(), DMPlexSetRegularRefinement()
8792 @*/
8793 PetscErrorCode DMPlexGetCellRefinerType(DM dm, DMPlexCellRefinerType *cr)
8794 {
8795   PetscFunctionBegin;
8796   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8797   PetscValidPointer(cr, 2);
8798   *cr = ((DM_Plex *) dm->data)->cellRefiner;
8799   PetscFunctionReturn(0);
8800 }
8801 
8802 /*@
8803   DMPlexSetCellRefinerType - Set the strategy for refining a cell
8804 
8805   Input Parameters:
8806 + dm - The DMPlex object
8807 - cr - The strategy number
8808 
8809   Level: intermediate
8810 
8811 .seealso: DMPlexGetCellRefinerType(), DMPlexGetRegularRefinement()
8812 @*/
8813 PetscErrorCode DMPlexSetCellRefinerType(DM dm, DMPlexCellRefinerType cr)
8814 {
8815   PetscFunctionBegin;
8816   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8817   ((DM_Plex *) dm->data)->cellRefiner = cr;
8818   PetscFunctionReturn(0);
8819 }
8820 
8821 /* anchors */
8822 /*@
8823   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
8824   call DMPlexGetAnchors() directly: if there are anchors, then DMPlexGetAnchors() is called during DMGetConstraints().
8825 
8826   not collective
8827 
8828   Input Parameters:
8829 . dm - The DMPlex object
8830 
8831   Output Parameters:
8832 + anchorSection - If not NULL, set to the section describing which points anchor the constrained points.
8833 - anchorIS - If not NULL, set to the list of anchors indexed by anchorSection
8834 
8835   Level: intermediate
8836 
8837 .seealso: DMPlexSetAnchors(), DMGetConstraints(), DMSetConstraints()
8838 @*/
8839 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
8840 {
8841   DM_Plex *plex = (DM_Plex *)dm->data;
8842   PetscErrorCode ierr;
8843 
8844   PetscFunctionBegin;
8845   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8846   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) {ierr = (*plex->createanchors)(dm);CHKERRQ(ierr);}
8847   if (anchorSection) *anchorSection = plex->anchorSection;
8848   if (anchorIS) *anchorIS = plex->anchorIS;
8849   PetscFunctionReturn(0);
8850 }
8851 
8852 /*@
8853   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.  Unlike boundary conditions,
8854   when a point's degrees of freedom in a section are constrained to an outside value, the anchor constraints set a
8855   point's degrees of freedom to be a linear combination of other points' degrees of freedom.
8856 
8857   After specifying the layout of constraints with DMPlexSetAnchors(), one specifies the constraints by calling
8858   DMGetConstraints() and filling in the entries in the constraint matrix.
8859 
8860   collective on dm
8861 
8862   Input Parameters:
8863 + dm - The DMPlex object
8864 . 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).
8865 - anchorIS - The list of all anchor points.  Must have a local communicator (PETSC_COMM_SELF or derivative).
8866 
8867   The reference counts of anchorSection and anchorIS are incremented.
8868 
8869   Level: intermediate
8870 
8871 .seealso: DMPlexGetAnchors(), DMGetConstraints(), DMSetConstraints()
8872 @*/
8873 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
8874 {
8875   DM_Plex        *plex = (DM_Plex *)dm->data;
8876   PetscMPIInt    result;
8877   PetscErrorCode ierr;
8878 
8879   PetscFunctionBegin;
8880   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8881   if (anchorSection) {
8882     PetscValidHeaderSpecific(anchorSection,PETSC_SECTION_CLASSID,2);
8883     ierr = MPI_Comm_compare(PETSC_COMM_SELF,PetscObjectComm((PetscObject)anchorSection),&result);CHKERRMPI(ierr);
8884     if (result != MPI_CONGRUENT && result != MPI_IDENT) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_NOTSAMECOMM,"anchor section must have local communicator");
8885   }
8886   if (anchorIS) {
8887     PetscValidHeaderSpecific(anchorIS,IS_CLASSID,3);
8888     ierr = MPI_Comm_compare(PETSC_COMM_SELF,PetscObjectComm((PetscObject)anchorIS),&result);CHKERRMPI(ierr);
8889     if (result != MPI_CONGRUENT && result != MPI_IDENT) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_NOTSAMECOMM,"anchor IS must have local communicator");
8890   }
8891 
8892   ierr = PetscObjectReference((PetscObject)anchorSection);CHKERRQ(ierr);
8893   ierr = PetscSectionDestroy(&plex->anchorSection);CHKERRQ(ierr);
8894   plex->anchorSection = anchorSection;
8895 
8896   ierr = PetscObjectReference((PetscObject)anchorIS);CHKERRQ(ierr);
8897   ierr = ISDestroy(&plex->anchorIS);CHKERRQ(ierr);
8898   plex->anchorIS = anchorIS;
8899 
8900   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
8901     PetscInt size, a, pStart, pEnd;
8902     const PetscInt *anchors;
8903 
8904     ierr = PetscSectionGetChart(anchorSection,&pStart,&pEnd);CHKERRQ(ierr);
8905     ierr = ISGetLocalSize(anchorIS,&size);CHKERRQ(ierr);
8906     ierr = ISGetIndices(anchorIS,&anchors);CHKERRQ(ierr);
8907     for (a = 0; a < size; a++) {
8908       PetscInt p;
8909 
8910       p = anchors[a];
8911       if (p >= pStart && p < pEnd) {
8912         PetscInt dof;
8913 
8914         ierr = PetscSectionGetDof(anchorSection,p,&dof);CHKERRQ(ierr);
8915         if (dof) {
8916           PetscErrorCode ierr2;
8917 
8918           ierr2 = ISRestoreIndices(anchorIS,&anchors);CHKERRQ(ierr2);
8919           SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Point %D cannot be constrained and an anchor",p);
8920         }
8921       }
8922     }
8923     ierr = ISRestoreIndices(anchorIS,&anchors);CHKERRQ(ierr);
8924   }
8925   /* reset the generic constraints */
8926   ierr = DMSetDefaultConstraints(dm,NULL,NULL);CHKERRQ(ierr);
8927   PetscFunctionReturn(0);
8928 }
8929 
8930 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
8931 {
8932   PetscSection anchorSection;
8933   PetscInt pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
8934   PetscErrorCode ierr;
8935 
8936   PetscFunctionBegin;
8937   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8938   ierr = DMPlexGetAnchors(dm,&anchorSection,NULL);CHKERRQ(ierr);
8939   ierr = PetscSectionCreate(PETSC_COMM_SELF,cSec);CHKERRQ(ierr);
8940   ierr = PetscSectionGetNumFields(section,&numFields);CHKERRQ(ierr);
8941   if (numFields) {
8942     PetscInt f;
8943     ierr = PetscSectionSetNumFields(*cSec,numFields);CHKERRQ(ierr);
8944 
8945     for (f = 0; f < numFields; f++) {
8946       PetscInt numComp;
8947 
8948       ierr = PetscSectionGetFieldComponents(section,f,&numComp);CHKERRQ(ierr);
8949       ierr = PetscSectionSetFieldComponents(*cSec,f,numComp);CHKERRQ(ierr);
8950     }
8951   }
8952   ierr = PetscSectionGetChart(anchorSection,&pStart,&pEnd);CHKERRQ(ierr);
8953   ierr = PetscSectionGetChart(section,&sStart,&sEnd);CHKERRQ(ierr);
8954   pStart = PetscMax(pStart,sStart);
8955   pEnd   = PetscMin(pEnd,sEnd);
8956   pEnd   = PetscMax(pStart,pEnd);
8957   ierr = PetscSectionSetChart(*cSec,pStart,pEnd);CHKERRQ(ierr);
8958   for (p = pStart; p < pEnd; p++) {
8959     ierr = PetscSectionGetDof(anchorSection,p,&dof);CHKERRQ(ierr);
8960     if (dof) {
8961       ierr = PetscSectionGetDof(section,p,&dof);CHKERRQ(ierr);
8962       ierr = PetscSectionSetDof(*cSec,p,dof);CHKERRQ(ierr);
8963       for (f = 0; f < numFields; f++) {
8964         ierr = PetscSectionGetFieldDof(section,p,f,&dof);CHKERRQ(ierr);
8965         ierr = PetscSectionSetFieldDof(*cSec,p,f,dof);CHKERRQ(ierr);
8966       }
8967     }
8968   }
8969   ierr = PetscSectionSetUp(*cSec);CHKERRQ(ierr);
8970   ierr = PetscObjectSetName((PetscObject) *cSec, "Constraint Section");CHKERRQ(ierr);
8971   PetscFunctionReturn(0);
8972 }
8973 
8974 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
8975 {
8976   PetscSection   aSec;
8977   PetscInt       pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
8978   const PetscInt *anchors;
8979   PetscInt       numFields, f;
8980   IS             aIS;
8981   PetscErrorCode ierr;
8982   MatType        mtype;
8983   PetscBool      iscuda,iskokkos;
8984 
8985   PetscFunctionBegin;
8986   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8987   ierr = PetscSectionGetStorageSize(cSec, &m);CHKERRQ(ierr);
8988   ierr = PetscSectionGetStorageSize(section, &n);CHKERRQ(ierr);
8989   ierr = MatCreate(PETSC_COMM_SELF,cMat);CHKERRQ(ierr);
8990   ierr = MatSetSizes(*cMat,m,n,m,n);CHKERRQ(ierr);
8991   ierr = PetscStrcmp(dm->mattype,MATSEQAIJCUSPARSE,&iscuda);CHKERRQ(ierr);
8992   if (!iscuda) { ierr = PetscStrcmp(dm->mattype,MATMPIAIJCUSPARSE,&iscuda);CHKERRQ(ierr); }
8993   ierr = PetscStrcmp(dm->mattype,MATSEQAIJKOKKOS,&iskokkos);CHKERRQ(ierr);
8994   if (!iskokkos) { ierr = PetscStrcmp(dm->mattype,MATMPIAIJKOKKOS,&iskokkos);CHKERRQ(ierr); }
8995   if (iscuda) mtype = MATSEQAIJCUSPARSE;
8996   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
8997   else mtype = MATSEQAIJ;
8998   ierr = MatSetType(*cMat,mtype);CHKERRQ(ierr);
8999   ierr = DMPlexGetAnchors(dm,&aSec,&aIS);CHKERRQ(ierr);
9000   ierr = ISGetIndices(aIS,&anchors);CHKERRQ(ierr);
9001   /* cSec will be a subset of aSec and section */
9002   ierr = PetscSectionGetChart(cSec,&pStart,&pEnd);CHKERRQ(ierr);
9003   ierr = PetscSectionGetChart(section,&sStart,&sEnd);CHKERRQ(ierr);
9004   ierr = PetscMalloc1(m+1,&i);CHKERRQ(ierr);
9005   i[0] = 0;
9006   ierr = PetscSectionGetNumFields(section,&numFields);CHKERRQ(ierr);
9007   for (p = pStart; p < pEnd; p++) {
9008     PetscInt rDof, rOff, r;
9009 
9010     ierr = PetscSectionGetDof(aSec,p,&rDof);CHKERRQ(ierr);
9011     if (!rDof) continue;
9012     ierr = PetscSectionGetOffset(aSec,p,&rOff);CHKERRQ(ierr);
9013     if (numFields) {
9014       for (f = 0; f < numFields; f++) {
9015         annz = 0;
9016         for (r = 0; r < rDof; r++) {
9017           a = anchors[rOff + r];
9018           if (a < sStart || a >= sEnd) continue;
9019           ierr = PetscSectionGetFieldDof(section,a,f,&aDof);CHKERRQ(ierr);
9020           annz += aDof;
9021         }
9022         ierr = PetscSectionGetFieldDof(cSec,p,f,&dof);CHKERRQ(ierr);
9023         ierr = PetscSectionGetFieldOffset(cSec,p,f,&off);CHKERRQ(ierr);
9024         for (q = 0; q < dof; q++) {
9025           i[off + q + 1] = i[off + q] + annz;
9026         }
9027       }
9028     }
9029     else {
9030       annz = 0;
9031       ierr = PetscSectionGetDof(cSec,p,&dof);CHKERRQ(ierr);
9032       for (q = 0; q < dof; q++) {
9033         a = anchors[rOff + q];
9034         if (a < sStart || a >= sEnd) continue;
9035         ierr = PetscSectionGetDof(section,a,&aDof);CHKERRQ(ierr);
9036         annz += aDof;
9037       }
9038       ierr = PetscSectionGetDof(cSec,p,&dof);CHKERRQ(ierr);
9039       ierr = PetscSectionGetOffset(cSec,p,&off);CHKERRQ(ierr);
9040       for (q = 0; q < dof; q++) {
9041         i[off + q + 1] = i[off + q] + annz;
9042       }
9043     }
9044   }
9045   nnz = i[m];
9046   ierr = PetscMalloc1(nnz,&j);CHKERRQ(ierr);
9047   offset = 0;
9048   for (p = pStart; p < pEnd; p++) {
9049     if (numFields) {
9050       for (f = 0; f < numFields; f++) {
9051         ierr = PetscSectionGetFieldDof(cSec,p,f,&dof);CHKERRQ(ierr);
9052         for (q = 0; q < dof; q++) {
9053           PetscInt rDof, rOff, r;
9054           ierr = PetscSectionGetDof(aSec,p,&rDof);CHKERRQ(ierr);
9055           ierr = PetscSectionGetOffset(aSec,p,&rOff);CHKERRQ(ierr);
9056           for (r = 0; r < rDof; r++) {
9057             PetscInt s;
9058 
9059             a = anchors[rOff + r];
9060             if (a < sStart || a >= sEnd) continue;
9061             ierr = PetscSectionGetFieldDof(section,a,f,&aDof);CHKERRQ(ierr);
9062             ierr = PetscSectionGetFieldOffset(section,a,f,&aOff);CHKERRQ(ierr);
9063             for (s = 0; s < aDof; s++) {
9064               j[offset++] = aOff + s;
9065             }
9066           }
9067         }
9068       }
9069     }
9070     else {
9071       ierr = PetscSectionGetDof(cSec,p,&dof);CHKERRQ(ierr);
9072       for (q = 0; q < dof; q++) {
9073         PetscInt rDof, rOff, r;
9074         ierr = PetscSectionGetDof(aSec,p,&rDof);CHKERRQ(ierr);
9075         ierr = PetscSectionGetOffset(aSec,p,&rOff);CHKERRQ(ierr);
9076         for (r = 0; r < rDof; r++) {
9077           PetscInt s;
9078 
9079           a = anchors[rOff + r];
9080           if (a < sStart || a >= sEnd) continue;
9081           ierr = PetscSectionGetDof(section,a,&aDof);CHKERRQ(ierr);
9082           ierr = PetscSectionGetOffset(section,a,&aOff);CHKERRQ(ierr);
9083           for (s = 0; s < aDof; s++) {
9084             j[offset++] = aOff + s;
9085           }
9086         }
9087       }
9088     }
9089   }
9090   ierr = MatSeqAIJSetPreallocationCSR(*cMat,i,j,NULL);CHKERRQ(ierr);
9091   ierr = PetscFree(i);CHKERRQ(ierr);
9092   ierr = PetscFree(j);CHKERRQ(ierr);
9093   ierr = ISRestoreIndices(aIS,&anchors);CHKERRQ(ierr);
9094   PetscFunctionReturn(0);
9095 }
9096 
9097 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
9098 {
9099   DM_Plex        *plex = (DM_Plex *)dm->data;
9100   PetscSection   anchorSection, section, cSec;
9101   Mat            cMat;
9102   PetscErrorCode ierr;
9103 
9104   PetscFunctionBegin;
9105   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9106   ierr = DMPlexGetAnchors(dm,&anchorSection,NULL);CHKERRQ(ierr);
9107   if (anchorSection) {
9108     PetscInt Nf;
9109 
9110     ierr = DMGetLocalSection(dm,&section);CHKERRQ(ierr);
9111     ierr = DMPlexCreateConstraintSection_Anchors(dm,section,&cSec);CHKERRQ(ierr);
9112     ierr = DMPlexCreateConstraintMatrix_Anchors(dm,section,cSec,&cMat);CHKERRQ(ierr);
9113     ierr = DMGetNumFields(dm,&Nf);CHKERRQ(ierr);
9114     if (Nf && plex->computeanchormatrix) {ierr = (*plex->computeanchormatrix)(dm,section,cSec,cMat);CHKERRQ(ierr);}
9115     ierr = DMSetDefaultConstraints(dm,cSec,cMat);CHKERRQ(ierr);
9116     ierr = PetscSectionDestroy(&cSec);CHKERRQ(ierr);
9117     ierr = MatDestroy(&cMat);CHKERRQ(ierr);
9118   }
9119   PetscFunctionReturn(0);
9120 }
9121 
9122 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
9123 {
9124   IS             subis;
9125   PetscSection   section, subsection;
9126   PetscErrorCode ierr;
9127 
9128   PetscFunctionBegin;
9129   ierr = DMGetLocalSection(dm, &section);CHKERRQ(ierr);
9130   if (!section) SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
9131   if (!subdm)   SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
9132   /* Create subdomain */
9133   ierr = DMPlexFilter(dm, label, value, subdm);CHKERRQ(ierr);
9134   /* Create submodel */
9135   ierr = DMPlexGetSubpointIS(*subdm, &subis);CHKERRQ(ierr);
9136   ierr = PetscSectionCreateSubmeshSection(section, subis, &subsection);CHKERRQ(ierr);
9137   ierr = DMSetLocalSection(*subdm, subsection);CHKERRQ(ierr);
9138   ierr = PetscSectionDestroy(&subsection);CHKERRQ(ierr);
9139   ierr = DMCopyDisc(dm, *subdm);CHKERRQ(ierr);
9140   /* Create map from submodel to global model */
9141   if (is) {
9142     PetscSection    sectionGlobal, subsectionGlobal;
9143     IS              spIS;
9144     const PetscInt *spmap;
9145     PetscInt       *subIndices;
9146     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
9147     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
9148 
9149     ierr = DMPlexGetSubpointIS(*subdm, &spIS);CHKERRQ(ierr);
9150     ierr = ISGetIndices(spIS, &spmap);CHKERRQ(ierr);
9151     ierr = PetscSectionGetNumFields(section, &Nf);CHKERRQ(ierr);
9152     ierr = DMGetGlobalSection(dm, &sectionGlobal);CHKERRQ(ierr);
9153     ierr = DMGetGlobalSection(*subdm, &subsectionGlobal);CHKERRQ(ierr);
9154     ierr = PetscSectionGetChart(subsection, &pStart, &pEnd);CHKERRQ(ierr);
9155     for (p = pStart; p < pEnd; ++p) {
9156       PetscInt gdof, pSubSize  = 0;
9157 
9158       ierr = PetscSectionGetDof(sectionGlobal, p, &gdof);CHKERRQ(ierr);
9159       if (gdof > 0) {
9160         for (f = 0; f < Nf; ++f) {
9161           PetscInt fdof, fcdof;
9162 
9163           ierr     = PetscSectionGetFieldDof(subsection, p, f, &fdof);CHKERRQ(ierr);
9164           ierr     = PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof);CHKERRQ(ierr);
9165           pSubSize += fdof-fcdof;
9166         }
9167         subSize += pSubSize;
9168         if (pSubSize) {
9169           if (bs < 0) {
9170             bs = pSubSize;
9171           } else if (bs != pSubSize) {
9172             /* Layout does not admit a pointwise block size */
9173             bs = 1;
9174           }
9175         }
9176       }
9177     }
9178     /* Must have same blocksize on all procs (some might have no points) */
9179     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs; bsLocal[1] = bs;
9180     ierr = PetscGlobalMinMaxInt(PetscObjectComm((PetscObject) dm), bsLocal, bsMinMax);CHKERRQ(ierr);
9181     if (bsMinMax[0] != bsMinMax[1]) {bs = 1;}
9182     else                            {bs = bsMinMax[0];}
9183     ierr = PetscMalloc1(subSize, &subIndices);CHKERRQ(ierr);
9184     for (p = pStart; p < pEnd; ++p) {
9185       PetscInt gdof, goff;
9186 
9187       ierr = PetscSectionGetDof(subsectionGlobal, p, &gdof);CHKERRQ(ierr);
9188       if (gdof > 0) {
9189         const PetscInt point = spmap[p];
9190 
9191         ierr = PetscSectionGetOffset(sectionGlobal, point, &goff);CHKERRQ(ierr);
9192         for (f = 0; f < Nf; ++f) {
9193           PetscInt fdof, fcdof, fc, f2, poff = 0;
9194 
9195           /* Can get rid of this loop by storing field information in the global section */
9196           for (f2 = 0; f2 < f; ++f2) {
9197             ierr  = PetscSectionGetFieldDof(section, p, f2, &fdof);CHKERRQ(ierr);
9198             ierr  = PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof);CHKERRQ(ierr);
9199             poff += fdof-fcdof;
9200           }
9201           ierr = PetscSectionGetFieldDof(section, p, f, &fdof);CHKERRQ(ierr);
9202           ierr = PetscSectionGetFieldConstraintDof(section, p, f, &fcdof);CHKERRQ(ierr);
9203           for (fc = 0; fc < fdof-fcdof; ++fc, ++subOff) {
9204             subIndices[subOff] = goff+poff+fc;
9205           }
9206         }
9207       }
9208     }
9209     ierr = ISRestoreIndices(spIS, &spmap);CHKERRQ(ierr);
9210     ierr = ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is);CHKERRQ(ierr);
9211     if (bs > 1) {
9212       /* We need to check that the block size does not come from non-contiguous fields */
9213       PetscInt i, j, set = 1;
9214       for (i = 0; i < subSize; i += bs) {
9215         for (j = 0; j < bs; ++j) {
9216           if (subIndices[i+j] != subIndices[i]+j) {set = 0; break;}
9217         }
9218       }
9219       if (set) {ierr = ISSetBlockSize(*is, bs);CHKERRQ(ierr);}
9220     }
9221     /* Attach nullspace */
9222     for (f = 0; f < Nf; ++f) {
9223       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
9224       if ((*subdm)->nullspaceConstructors[f]) break;
9225     }
9226     if (f < Nf) {
9227       MatNullSpace nullSpace;
9228       ierr = (*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace);CHKERRQ(ierr);
9229 
9230       ierr = PetscObjectCompose((PetscObject) *is, "nullspace", (PetscObject) nullSpace);CHKERRQ(ierr);
9231       ierr = MatNullSpaceDestroy(&nullSpace);CHKERRQ(ierr);
9232     }
9233   }
9234   PetscFunctionReturn(0);
9235 }
9236 
9237 /*@
9238   DMPlexMonitorThroughput - Report the cell throughput of FE integration
9239 
9240   Input Parameter:
9241 - dm - The DM
9242 
9243   Level: developer
9244 
9245   Options Database Keys:
9246 . -dm_plex_monitor_throughput - Activate the monitor
9247 
9248 .seealso: DMSetFromOptions(), DMPlexCreate()
9249 @*/
9250 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
9251 {
9252 #if defined(PETSC_USE_LOG)
9253   PetscStageLog      stageLog;
9254   PetscLogEvent      event;
9255   PetscLogStage      stage;
9256   PetscEventPerfInfo eventInfo;
9257   PetscReal          cellRate, flopRate;
9258   PetscInt           cStart, cEnd, Nf, N;
9259   const char        *name;
9260   PetscErrorCode     ierr;
9261 #endif
9262 
9263   PetscFunctionBegin;
9264   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9265 #if defined(PETSC_USE_LOG)
9266   ierr = PetscObjectGetName((PetscObject) dm, &name);CHKERRQ(ierr);
9267   ierr = DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd);CHKERRQ(ierr);
9268   ierr = DMGetNumFields(dm, &Nf);CHKERRQ(ierr);
9269   ierr = PetscLogGetStageLog(&stageLog);CHKERRQ(ierr);
9270   ierr = PetscStageLogGetCurrent(stageLog, &stage);CHKERRQ(ierr);
9271   ierr = PetscLogEventGetId("DMPlexResidualFE", &event);CHKERRQ(ierr);
9272   ierr = PetscLogEventGetPerfInfo(stage, event, &eventInfo);CHKERRQ(ierr);
9273   N        = (cEnd - cStart)*Nf*eventInfo.count;
9274   flopRate = eventInfo.flops/eventInfo.time;
9275   cellRate = N/eventInfo.time;
9276   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);
9277 #else
9278   SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Plex Throughput Monitor is not supported if logging is turned off. Reconfigure using --with-log.");
9279 #endif
9280   PetscFunctionReturn(0);
9281 }
9282