xref: /petsc/src/dm/impls/plex/plex.c (revision f1f2ae845fd5815e1ad61e968c2c51b97d3840d2)
1 #include <petsc/private/dmpleximpl.h>   /*I      "petscdmplex.h"   I*/
2 #include <petsc/private/isimpl.h>
3 #include <petsc/private/vecimpl.h>
4 #include <petsc/private/glvisvecimpl.h>
5 #include <petscsf.h>
6 #include <petscds.h>
7 #include <petscdraw.h>
8 #include <petscdmfield.h>
9 #include <petscdmplextransform.h>
10 
11 /* Logging support */
12 PetscLogEvent DMPLEX_Interpolate, DMPLEX_Partition, DMPLEX_Distribute, DMPLEX_DistributeCones, DMPLEX_DistributeLabels, DMPLEX_DistributeSF, DMPLEX_DistributeOverlap, DMPLEX_DistributeField, DMPLEX_DistributeData, DMPLEX_Migrate, DMPLEX_InterpolateSF, DMPLEX_GlobalToNaturalBegin, DMPLEX_GlobalToNaturalEnd, DMPLEX_NaturalToGlobalBegin, DMPLEX_NaturalToGlobalEnd, DMPLEX_Stratify, DMPLEX_Symmetrize, DMPLEX_Preallocate, DMPLEX_ResidualFEM, DMPLEX_JacobianFEM, DMPLEX_InterpolatorFEM, DMPLEX_InjectorFEM, DMPLEX_IntegralFEM, DMPLEX_CreateGmsh, DMPLEX_RebalanceSharedPoints, DMPLEX_PartSelf, DMPLEX_PartLabelInvert, DMPLEX_PartLabelCreateSF, DMPLEX_PartStratSF, DMPLEX_CreatePointSF,DMPLEX_LocatePoints,DMPLEX_TopologyView,DMPLEX_LabelsView,DMPLEX_CoordinatesView,DMPLEX_SectionView,DMPLEX_GlobalVectorView,DMPLEX_LocalVectorView,DMPLEX_TopologyLoad,DMPLEX_LabelsLoad,DMPLEX_CoordinatesLoad,DMPLEX_SectionLoad,DMPLEX_GlobalVectorLoad,DMPLEX_LocalVectorLoad;
13 
14 PETSC_EXTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
15 
16 /*@
17   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
18 
19   Input Parameter:
20 . dm      - The DMPlex object
21 
22   Output Parameter:
23 . simplex - Flag checking for a simplex
24 
25   Note: This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
26   If the mesh has no cells, this returns PETSC_FALSE.
27 
28   Level: intermediate
29 
30 .seealso `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
31 @*/
32 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
33 {
34   DMPolytopeType ct;
35   PetscInt       cStart, cEnd;
36 
37   PetscFunctionBegin;
38   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
39   if (cEnd <= cStart) {*simplex = PETSC_FALSE; PetscFunctionReturn(0);}
40   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
41   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct)+1 ? PETSC_TRUE : PETSC_FALSE;
42   PetscFunctionReturn(0);
43 }
44 
45 /*@
46   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
47 
48   Input Parameters:
49 + dm     - The DMPlex object
50 - height - The cell height in the Plex, 0 is the default
51 
52   Output Parameters:
53 + cStart - The first "normal" cell
54 - cEnd   - The upper bound on "normal"" cells
55 
56   Note: This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
57 
58   Level: developer
59 
60 .seealso `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
61 @*/
62 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd)
63 {
64   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
65   PetscInt       cS, cE, c;
66 
67   PetscFunctionBegin;
68   PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), &cS, &cE));
69   for (c = cS; c < cE; ++c) {
70     DMPolytopeType cct;
71 
72     PetscCall(DMPlexGetCellType(dm, c, &cct));
73     if ((PetscInt) cct < 0) break;
74     switch (cct) {
75       case DM_POLYTOPE_POINT:
76       case DM_POLYTOPE_SEGMENT:
77       case DM_POLYTOPE_TRIANGLE:
78       case DM_POLYTOPE_QUADRILATERAL:
79       case DM_POLYTOPE_TETRAHEDRON:
80       case DM_POLYTOPE_HEXAHEDRON:
81         ct = cct;
82         break;
83       default: break;
84     }
85     if (ct != DM_POLYTOPE_UNKNOWN) break;
86   }
87   if (ct != DM_POLYTOPE_UNKNOWN) {
88     DMLabel ctLabel;
89 
90     PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
91     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &cS, &cE));
92   }
93   if (cStart) *cStart = cS;
94   if (cEnd)   *cEnd   = cE;
95   PetscFunctionReturn(0);
96 }
97 
98 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
99 {
100   PetscInt       cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
101   PetscInt       vcdof[2] = {0,0}, globalvcdof[2];
102 
103   PetscFunctionBegin;
104   *ft  = PETSC_VTK_INVALID;
105   PetscCall(DMGetCoordinateDim(dm, &cdim));
106   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
107   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
108   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
109   if (field >= 0) {
110     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
111     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
112   } else {
113     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
114     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
115   }
116   PetscCallMPI(MPI_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
117   if (globalvcdof[0]) {
118     *sStart = vStart;
119     *sEnd   = vEnd;
120     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
121     else                        *ft = PETSC_VTK_POINT_FIELD;
122   } else if (globalvcdof[1]) {
123     *sStart = cStart;
124     *sEnd   = cEnd;
125     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
126     else                        *ft = PETSC_VTK_CELL_FIELD;
127   } else {
128     if (field >= 0) {
129       const char *fieldname;
130 
131       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
132       PetscCall(PetscInfo((PetscObject) dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
133     } else {
134       PetscCall(PetscInfo((PetscObject) dm, "Could not classify VTK output type of section\n"));
135     }
136   }
137   PetscFunctionReturn(0);
138 }
139 
140 /*@
141   DMPlexVecView1D - Plot many 1D solutions on the same line graph
142 
143   Collective on dm
144 
145   Input Parameters:
146 + dm - The DMPlex
147 . n  - The number of vectors
148 . u  - The array of local vectors
149 - viewer - The Draw viewer
150 
151   Level: advanced
152 
153 .seealso: `VecViewFromOptions()`, `VecView()`
154 @*/
155 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
156 {
157   PetscDS            ds;
158   PetscDraw          draw = NULL;
159   PetscDrawLG        lg;
160   Vec                coordinates;
161   const PetscScalar *coords, **sol;
162   PetscReal         *vals;
163   PetscInt          *Nc;
164   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
165   char             **names;
166 
167   PetscFunctionBegin;
168   PetscCall(DMGetDS(dm, &ds));
169   PetscCall(PetscDSGetNumFields(ds, &Nf));
170   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
171   PetscCall(PetscDSGetComponents(ds, &Nc));
172 
173   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
174   if (!draw) PetscFunctionReturn(0);
175   PetscCall(PetscDrawLGCreate(draw, n*Nl, &lg));
176 
177   PetscCall(PetscMalloc3(n, &sol, n*Nl, &names, n*Nl, &vals));
178   for (i = 0, l = 0; i < n; ++i) {
179     const char *vname;
180 
181     PetscCall(PetscObjectGetName((PetscObject) u[i], &vname));
182     for (f = 0; f < Nf; ++f) {
183       PetscObject disc;
184       const char *fname;
185       char        tmpname[PETSC_MAX_PATH_LEN];
186 
187       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
188       /* TODO Create names for components */
189       for (c = 0; c < Nc[f]; ++c, ++l) {
190         PetscCall(PetscObjectGetName(disc, &fname));
191         PetscCall(PetscStrcpy(tmpname, vname));
192         PetscCall(PetscStrlcat(tmpname, ":", PETSC_MAX_PATH_LEN));
193         PetscCall(PetscStrlcat(tmpname, fname, PETSC_MAX_PATH_LEN));
194         PetscCall(PetscStrallocpy(tmpname, &names[l]));
195       }
196     }
197   }
198   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *) names));
199   /* Just add P_1 support for now */
200   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
201   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
202   PetscCall(VecGetArrayRead(coordinates, &coords));
203   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
204   for (v = vStart; v < vEnd; ++v) {
205     PetscScalar *x, *svals;
206 
207     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
208     for (i = 0; i < n; ++i) {
209       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
210       for (l = 0; l < Nl; ++l) vals[i*Nl + l] = PetscRealPart(svals[l]);
211     }
212     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
213   }
214   PetscCall(VecRestoreArrayRead(coordinates, &coords));
215   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
216   for (l = 0; l < n*Nl; ++l) PetscCall(PetscFree(names[l]));
217   PetscCall(PetscFree3(sol, names, vals));
218 
219   PetscCall(PetscDrawLGDraw(lg));
220   PetscCall(PetscDrawLGDestroy(&lg));
221   PetscFunctionReturn(0);
222 }
223 
224 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
225 {
226   DM             dm;
227 
228   PetscFunctionBegin;
229   PetscCall(VecGetDM(u, &dm));
230   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
231   PetscFunctionReturn(0);
232 }
233 
234 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
235 {
236   DM                 dm;
237   PetscSection       s;
238   PetscDraw          draw, popup;
239   DM                 cdm;
240   PetscSection       coordSection;
241   Vec                coordinates;
242   const PetscScalar *coords, *array;
243   PetscReal          bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
244   PetscReal          vbound[2], time;
245   PetscBool          flg;
246   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
247   const char        *name;
248   char               title[PETSC_MAX_PATH_LEN];
249 
250   PetscFunctionBegin;
251   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
252   PetscCall(VecGetDM(v, &dm));
253   PetscCall(DMGetCoordinateDim(dm, &dim));
254   PetscCall(DMGetLocalSection(dm, &s));
255   PetscCall(PetscSectionGetNumFields(s, &Nf));
256   PetscCall(DMGetCoarsenLevel(dm, &level));
257   PetscCall(DMGetCoordinateDM(dm, &cdm));
258   PetscCall(DMGetLocalSection(cdm, &coordSection));
259   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
260   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
261   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
262 
263   PetscCall(PetscObjectGetName((PetscObject) v, &name));
264   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
265 
266   PetscCall(VecGetLocalSize(coordinates, &N));
267   PetscCall(VecGetArrayRead(coordinates, &coords));
268   for (c = 0; c < N; c += dim) {
269     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));   bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
270     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c+1])); bound[3] = PetscMax(bound[3], PetscRealPart(coords[c+1]));
271   }
272   PetscCall(VecRestoreArrayRead(coordinates, &coords));
273   PetscCall(PetscDrawClear(draw));
274 
275   /* Could implement something like DMDASelectFields() */
276   for (f = 0; f < Nf; ++f) {
277     DM   fdm = dm;
278     Vec  fv  = v;
279     IS   fis;
280     char prefix[PETSC_MAX_PATH_LEN];
281     const char *fname;
282 
283     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
284     PetscCall(PetscSectionGetFieldName(s, f, &fname));
285 
286     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix,sizeof(prefix)));
287     else               {prefix[0] = '\0';}
288     if (Nf > 1) {
289       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
290       PetscCall(VecGetSubVector(v, fis, &fv));
291       PetscCall(PetscStrlcat(prefix, fname,sizeof(prefix)));
292       PetscCall(PetscStrlcat(prefix, "_",sizeof(prefix)));
293     }
294     for (comp = 0; comp < Nc; ++comp, ++w) {
295       PetscInt nmax = 2;
296 
297       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
298       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
299       else        PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
300       PetscCall(PetscDrawSetTitle(draw, title));
301 
302       /* TODO Get max and min only for this component */
303       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
304       if (!flg) {
305         PetscCall(VecMin(fv, NULL, &vbound[0]));
306         PetscCall(VecMax(fv, NULL, &vbound[1]));
307         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
308       }
309       PetscCall(PetscDrawGetPopup(draw, &popup));
310       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
311       PetscCall(PetscDrawSetCoordinates(draw, bound[0], bound[1], bound[2], bound[3]));
312 
313       PetscCall(VecGetArrayRead(fv, &array));
314       for (c = cStart; c < cEnd; ++c) {
315         PetscScalar *coords = NULL, *a = NULL;
316         PetscInt     numCoords, color[4] = {-1,-1,-1,-1};
317 
318         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
319         if (a) {
320           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
321           color[1] = color[2] = color[3] = color[0];
322         } else {
323           PetscScalar *vals = NULL;
324           PetscInt     numVals, va;
325 
326           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
327           PetscCheck(numVals % Nc == 0,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "The number of components %" PetscInt_FMT " does not divide the number of values in the closure %" PetscInt_FMT, Nc, numVals);
328           switch (numVals/Nc) {
329           case 3: /* P1 Triangle */
330           case 4: /* P1 Quadrangle */
331             for (va = 0; va < numVals/Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va*Nc+comp]), vbound[0], vbound[1]);
332             break;
333           case 6: /* P2 Triangle */
334           case 8: /* P2 Quadrangle */
335             for (va = 0; va < numVals/(Nc*2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va*Nc+comp + numVals/(Nc*2)]), vbound[0], vbound[1]);
336             break;
337           default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals/Nc);
338           }
339           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
340         }
341         PetscCall(DMPlexVecGetClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
342         switch (numCoords) {
343         case 6:
344         case 12: /* Localized triangle */
345           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
346           break;
347         case 8:
348         case 16: /* Localized quadrilateral */
349           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
350           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), color[2], color[3], color[0]));
351           break;
352         default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
353         }
354         PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
355       }
356       PetscCall(VecRestoreArrayRead(fv, &array));
357       PetscCall(PetscDrawFlush(draw));
358       PetscCall(PetscDrawPause(draw));
359       PetscCall(PetscDrawSave(draw));
360     }
361     if (Nf > 1) {
362       PetscCall(VecRestoreSubVector(v, fis, &fv));
363       PetscCall(ISDestroy(&fis));
364       PetscCall(DMDestroy(&fdm));
365     }
366   }
367   PetscFunctionReturn(0);
368 }
369 
370 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
371 {
372   DM        dm;
373   PetscDraw draw;
374   PetscInt  dim;
375   PetscBool isnull;
376 
377   PetscFunctionBegin;
378   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
379   PetscCall(PetscDrawIsNull(draw, &isnull));
380   if (isnull) PetscFunctionReturn(0);
381 
382   PetscCall(VecGetDM(v, &dm));
383   PetscCall(DMGetCoordinateDim(dm, &dim));
384   switch (dim) {
385   case 1: PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));break;
386   case 2: PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));break;
387   default: SETERRQ(PetscObjectComm((PetscObject) v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
388   }
389   PetscFunctionReturn(0);
390 }
391 
392 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
393 {
394   DM                      dm;
395   Vec                     locv;
396   const char              *name;
397   PetscSection            section;
398   PetscInt                pStart, pEnd;
399   PetscInt                numFields;
400   PetscViewerVTKFieldType ft;
401 
402   PetscFunctionBegin;
403   PetscCall(VecGetDM(v, &dm));
404   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
405   PetscCall(PetscObjectGetName((PetscObject) v, &name));
406   PetscCall(PetscObjectSetName((PetscObject) locv, name));
407   PetscCall(VecCopy(v, locv));
408   PetscCall(DMGetLocalSection(dm, &section));
409   PetscCall(PetscSectionGetNumFields(section, &numFields));
410   if (!numFields) {
411     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
412     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject) dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE,(PetscObject) locv));
413   } else {
414     PetscInt f;
415 
416     for (f = 0; f < numFields; f++) {
417       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
418       if (ft == PETSC_VTK_INVALID) continue;
419       PetscCall(PetscObjectReference((PetscObject)locv));
420       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject) dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE,(PetscObject) locv));
421     }
422     PetscCall(VecDestroy(&locv));
423   }
424   PetscFunctionReturn(0);
425 }
426 
427 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
428 {
429   DM             dm;
430   PetscBool      isvtk, ishdf5, isdraw, isglvis;
431 
432   PetscFunctionBegin;
433   PetscCall(VecGetDM(v, &dm));
434   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
435   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,   &isvtk));
436   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,  &ishdf5));
437   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERDRAW,  &isdraw));
438   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERGLVIS, &isglvis));
439   if (isvtk || ishdf5 || isdraw || isglvis) {
440     PetscInt    i,numFields;
441     PetscObject fe;
442     PetscBool   fem = PETSC_FALSE;
443     Vec         locv = v;
444     const char  *name;
445     PetscInt    step;
446     PetscReal   time;
447 
448     PetscCall(DMGetNumFields(dm, &numFields));
449     for (i=0; i<numFields; i++) {
450       PetscCall(DMGetField(dm, i, NULL, &fe));
451       if (fe->classid == PETSCFE_CLASSID) { fem = PETSC_TRUE; break; }
452     }
453     if (fem) {
454       PetscObject isZero;
455 
456       PetscCall(DMGetLocalVector(dm, &locv));
457       PetscCall(PetscObjectGetName((PetscObject) v, &name));
458       PetscCall(PetscObjectSetName((PetscObject) locv, name));
459       PetscCall(PetscObjectQuery((PetscObject) v, "__Vec_bc_zero__", &isZero));
460       PetscCall(PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", isZero));
461       PetscCall(VecCopy(v, locv));
462       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
463       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
464     }
465     if (isvtk) {
466       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
467     } else if (ishdf5) {
468 #if defined(PETSC_HAVE_HDF5)
469       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
470 #else
471       SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
472 #endif
473     } else if (isdraw) {
474       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
475     } else if (isglvis) {
476       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
477       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
478       PetscCall(VecView_GLVis(locv, viewer));
479     }
480     if (fem) {
481       PetscCall(PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", NULL));
482       PetscCall(DMRestoreLocalVector(dm, &locv));
483     }
484   } else {
485     PetscBool isseq;
486 
487     PetscCall(PetscObjectTypeCompare((PetscObject) v, VECSEQ, &isseq));
488     if (isseq) PetscCall(VecView_Seq(v, viewer));
489     else       PetscCall(VecView_MPI(v, viewer));
490   }
491   PetscFunctionReturn(0);
492 }
493 
494 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
495 {
496   DM        dm;
497   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii;
498 
499   PetscFunctionBegin;
500   PetscCall(VecGetDM(v, &dm));
501   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
502   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,      &isvtk));
503   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,     &ishdf5));
504   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERDRAW,     &isdraw));
505   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERGLVIS,    &isglvis));
506   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWEREXODUSII, &isexodusii));
507   if (isvtk || isdraw || isglvis) {
508     Vec         locv;
509     PetscObject isZero;
510     const char *name;
511 
512     PetscCall(DMGetLocalVector(dm, &locv));
513     PetscCall(PetscObjectGetName((PetscObject) v, &name));
514     PetscCall(PetscObjectSetName((PetscObject) locv, name));
515     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
516     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
517     PetscCall(PetscObjectQuery((PetscObject) v, "__Vec_bc_zero__", &isZero));
518     PetscCall(PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", isZero));
519     PetscCall(VecView_Plex_Local(locv, viewer));
520     PetscCall(PetscObjectCompose((PetscObject) locv, "__Vec_bc_zero__", NULL));
521     PetscCall(DMRestoreLocalVector(dm, &locv));
522   } else if (ishdf5) {
523 #if defined(PETSC_HAVE_HDF5)
524     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
525 #else
526     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
527 #endif
528   } else if (isexodusii) {
529 #if defined(PETSC_HAVE_EXODUSII)
530     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
531 #else
532     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
533 #endif
534   } else {
535     PetscBool isseq;
536 
537     PetscCall(PetscObjectTypeCompare((PetscObject) v, VECSEQ, &isseq));
538     if (isseq) PetscCall(VecView_Seq(v, viewer));
539     else       PetscCall(VecView_MPI(v, viewer));
540   }
541   PetscFunctionReturn(0);
542 }
543 
544 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
545 {
546   DM                dm;
547   MPI_Comm          comm;
548   PetscViewerFormat format;
549   Vec               v;
550   PetscBool         isvtk, ishdf5;
551 
552   PetscFunctionBegin;
553   PetscCall(VecGetDM(originalv, &dm));
554   PetscCall(PetscObjectGetComm((PetscObject) originalv, &comm));
555   PetscCheck(dm,comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
556   PetscCall(PetscViewerGetFormat(viewer, &format));
557   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
558   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,  &isvtk));
559   if (format == PETSC_VIEWER_NATIVE) {
560     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
561     /* this need a better fix */
562     if (dm->useNatural) {
563       if (dm->sfNatural) {
564         const char *vecname;
565         PetscInt    n, nroots;
566 
567         PetscCall(VecGetLocalSize(originalv, &n));
568         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
569         if (n == nroots) {
570           PetscCall(DMGetGlobalVector(dm, &v));
571           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
572           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
573           PetscCall(PetscObjectGetName((PetscObject) originalv, &vecname));
574           PetscCall(PetscObjectSetName((PetscObject) v, vecname));
575         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
576       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
577     } else v = originalv;
578   } else v = originalv;
579 
580   if (ishdf5) {
581 #if defined(PETSC_HAVE_HDF5)
582     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
583 #else
584     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
585 #endif
586   } else if (isvtk) {
587     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
588   } else {
589     PetscBool isseq;
590 
591     PetscCall(PetscObjectTypeCompare((PetscObject) v, VECSEQ, &isseq));
592     if (isseq) PetscCall(VecView_Seq(v, viewer));
593     else       PetscCall(VecView_MPI(v, viewer));
594   }
595   if (v != originalv) PetscCall(DMRestoreGlobalVector(dm, &v));
596   PetscFunctionReturn(0);
597 }
598 
599 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
600 {
601   DM             dm;
602   PetscBool      ishdf5;
603 
604   PetscFunctionBegin;
605   PetscCall(VecGetDM(v, &dm));
606   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
607   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
608   if (ishdf5) {
609     DM          dmBC;
610     Vec         gv;
611     const char *name;
612 
613     PetscCall(DMGetOutputDM(dm, &dmBC));
614     PetscCall(DMGetGlobalVector(dmBC, &gv));
615     PetscCall(PetscObjectGetName((PetscObject) v, &name));
616     PetscCall(PetscObjectSetName((PetscObject) gv, name));
617     PetscCall(VecLoad_Default(gv, viewer));
618     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
619     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
620     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
621   } else PetscCall(VecLoad_Default(v, viewer));
622   PetscFunctionReturn(0);
623 }
624 
625 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
626 {
627   DM             dm;
628   PetscBool      ishdf5,isexodusii;
629 
630   PetscFunctionBegin;
631   PetscCall(VecGetDM(v, &dm));
632   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
633   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,     &ishdf5));
634   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWEREXODUSII, &isexodusii));
635   if (ishdf5) {
636 #if defined(PETSC_HAVE_HDF5)
637     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
638 #else
639     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
640 #endif
641   } else if (isexodusii) {
642 #if defined(PETSC_HAVE_EXODUSII)
643     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
644 #else
645     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
646 #endif
647   } else PetscCall(VecLoad_Default(v, viewer));
648   PetscFunctionReturn(0);
649 }
650 
651 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
652 {
653   DM                dm;
654   PetscViewerFormat format;
655   PetscBool         ishdf5;
656 
657   PetscFunctionBegin;
658   PetscCall(VecGetDM(originalv, &dm));
659   PetscCheck(dm,PetscObjectComm((PetscObject) originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
660   PetscCall(PetscViewerGetFormat(viewer, &format));
661   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
662   if (format == PETSC_VIEWER_NATIVE) {
663     if (dm->useNatural) {
664       if (dm->sfNatural) {
665         if (ishdf5) {
666 #if defined(PETSC_HAVE_HDF5)
667           Vec         v;
668           const char *vecname;
669 
670           PetscCall(DMGetGlobalVector(dm, &v));
671           PetscCall(PetscObjectGetName((PetscObject) originalv, &vecname));
672           PetscCall(PetscObjectSetName((PetscObject) v, vecname));
673           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
674           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
675           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
676           PetscCall(DMRestoreGlobalVector(dm, &v));
677 #else
678           SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
679 #endif
680         } else SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
681       }
682     } else PetscCall(VecLoad_Default(originalv, viewer));
683   }
684   PetscFunctionReturn(0);
685 }
686 
687 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
688 {
689   PetscSection       coordSection;
690   Vec                coordinates;
691   DMLabel            depthLabel, celltypeLabel;
692   const char        *name[4];
693   const PetscScalar *a;
694   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
695 
696   PetscFunctionBegin;
697   PetscCall(DMGetDimension(dm, &dim));
698   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
699   PetscCall(DMGetCoordinateSection(dm, &coordSection));
700   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
701   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
702   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
703   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
704   PetscCall(VecGetArrayRead(coordinates, &a));
705   name[0]     = "vertex";
706   name[1]     = "edge";
707   name[dim-1] = "face";
708   name[dim]   = "cell";
709   for (c = cStart; c < cEnd; ++c) {
710     PetscInt *closure = NULL;
711     PetscInt  closureSize, cl, ct;
712 
713     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
714     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
715     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
716     PetscCall(PetscViewerASCIIPushTab(viewer));
717     for (cl = 0; cl < closureSize*2; cl += 2) {
718       PetscInt point = closure[cl], depth, dof, off, d, p;
719 
720       if ((point < pStart) || (point >= pEnd)) continue;
721       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
722       if (!dof) continue;
723       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
724       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
725       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
726       for (p = 0; p < dof/dim; ++p) {
727         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
728         for (d = 0; d < dim; ++d) {
729           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
730           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double) PetscRealPart(a[off+p*dim+d])));
731         }
732         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
733       }
734       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
735     }
736     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
737     PetscCall(PetscViewerASCIIPopTab(viewer));
738   }
739   PetscCall(VecRestoreArrayRead(coordinates, &a));
740   PetscFunctionReturn(0);
741 }
742 
743 typedef enum {CS_CARTESIAN, CS_POLAR, CS_CYLINDRICAL, CS_SPHERICAL} CoordSystem;
744 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
745 
746 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
747 {
748   PetscInt       i;
749 
750   PetscFunctionBegin;
751   if (dim > 3) {
752     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double) PetscRealPart(x[i])));
753   } else {
754     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
755 
756     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
757     switch (cs) {
758       case CS_CARTESIAN: for (i = 0; i < dim; ++i) trcoords[i] = coords[i];break;
759       case CS_POLAR:
760         PetscCheck(dim == 2,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
761         trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
762         trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
763         break;
764       case CS_CYLINDRICAL:
765         PetscCheck(dim == 3,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
766         trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
767         trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
768         trcoords[2] = coords[2];
769         break;
770       case CS_SPHERICAL:
771         PetscCheck(dim == 3,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
772         trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
773         trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
774         trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
775         break;
776     }
777     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double) trcoords[i]));
778   }
779   PetscFunctionReturn(0);
780 }
781 
782 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
783 {
784   DM_Plex          *mesh = (DM_Plex*) dm->data;
785   DM                cdm, cdmCell;
786   PetscSection      coordSection, coordSectionCell;
787   Vec               coordinates, coordinatesCell;
788   PetscViewerFormat format;
789 
790   PetscFunctionBegin;
791   PetscCall(DMGetCoordinateDM(dm, &cdm));
792   PetscCall(DMGetCoordinateSection(dm, &coordSection));
793   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
794   PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
795   PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
796   PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
797   PetscCall(PetscViewerGetFormat(viewer, &format));
798   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
799     const char *name;
800     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
801     PetscInt    pStart, pEnd, p, numLabels, l;
802     PetscMPIInt rank, size;
803 
804     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
805     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
806     PetscCall(PetscObjectGetName((PetscObject) dm, &name));
807     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
808     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
809     PetscCall(DMGetDimension(dm, &dim));
810     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
811     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
812     else      PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
813     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
814     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
815     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
816     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
817     for (p = pStart; p < pEnd; ++p) {
818       PetscInt dof, off, s;
819 
820       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
821       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
822       for (s = off; s < off+dof; ++s) {
823         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
824       }
825     }
826     PetscCall(PetscViewerFlush(viewer));
827     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
828     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
829     for (p = pStart; p < pEnd; ++p) {
830       PetscInt dof, off, c;
831 
832       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
833       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
834       for (c = off; c < off+dof; ++c) {
835         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " <---- %" PetscInt_FMT " (%" PetscInt_FMT ")\n", rank, p, mesh->cones[c], mesh->coneOrientations[c]));
836       }
837     }
838     PetscCall(PetscViewerFlush(viewer));
839     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
840     if (coordSection && coordinates) {
841       CoordSystem        cs = CS_CARTESIAN;
842       const PetscScalar *array, *arrayCell = NULL;
843       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
844       PetscMPIInt        rank;
845       const char        *name;
846 
847       PetscCall(PetscOptionsGetEnum(((PetscObject) viewer)->options, ((PetscObject) viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *) &cs, NULL));
848       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
849       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
850       PetscCheck(Nf == 1,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
851       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
852       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
853       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
854       pStart =  PetscMin(pvStart, pcStart);
855       pEnd   =  PetscMax(pvEnd,   pcEnd);
856       PetscCall(PetscObjectGetName((PetscObject) coordinates, &name));
857       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
858       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
859       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
860 
861       PetscCall(VecGetArrayRead(coordinates, &array));
862       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
863       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
864       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
865       for (p = pStart; p < pEnd; ++p) {
866         PetscInt dof, off;
867 
868         if (p >= pvStart && p < pvEnd) {
869           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
870           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
871           if (dof) {
872             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
873             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
874             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
875           }
876         }
877         if (cdmCell && p >= pcStart && p < pcEnd) {
878           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
879           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
880           if (dof) {
881             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
882             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
883             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
884           }
885         }
886       }
887       PetscCall(PetscViewerFlush(viewer));
888       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
889       PetscCall(VecRestoreArrayRead(coordinates, &array));
890       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
891     }
892     PetscCall(DMGetNumLabels(dm, &numLabels));
893     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
894     for (l = 0; l < numLabels; ++l) {
895       DMLabel     label;
896       PetscBool   isdepth;
897       const char *name;
898 
899       PetscCall(DMGetLabelName(dm, l, &name));
900       PetscCall(PetscStrcmp(name, "depth", &isdepth));
901       if (isdepth) continue;
902       PetscCall(DMGetLabel(dm, name, &label));
903       PetscCall(DMLabelView(label, viewer));
904     }
905     if (size > 1) {
906       PetscSF sf;
907 
908       PetscCall(DMGetPointSF(dm, &sf));
909       PetscCall(PetscSFView(sf, viewer));
910     }
911     PetscCall(PetscViewerFlush(viewer));
912   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
913     const char  *name, *color;
914     const char  *defcolors[3]  = {"gray", "orange", "green"};
915     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
916     char         lname[PETSC_MAX_PATH_LEN];
917     PetscReal    scale         = 2.0;
918     PetscReal    tikzscale     = 1.0;
919     PetscBool    useNumbers    = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
920     double       tcoords[3];
921     PetscScalar *coords;
922     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
923     PetscMPIInt  rank, size;
924     char         **names, **colors, **lcolors;
925     PetscBool    flg, lflg;
926     PetscBT      wp = NULL;
927     PetscInt     pEnd, pStart;
928 
929     PetscCall(DMGetDimension(dm, &dim));
930     PetscCall(DMPlexGetDepth(dm, &depth));
931     PetscCall(DMGetNumLabels(dm, &numLabels));
932     numLabels  = PetscMax(numLabels, 10);
933     numColors  = 10;
934     numLColors = 10;
935     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
936     PetscCall(PetscOptionsGetReal(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
937     PetscCall(PetscOptionsGetReal(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
938     PetscCall(PetscOptionsGetBool(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
939     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
940     for (d = 0; d < 4; ++d) drawColors[d]  = PETSC_TRUE;
941     n = 4;
942     PetscCall(PetscOptionsGetBoolArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
943     PetscCheck(!flg || n == dim+1,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim+1);
944     PetscCall(PetscOptionsGetBoolArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
945     PetscCheck(!flg || n == dim+1,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim+1);
946     PetscCall(PetscOptionsGetStringArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
947     if (!useLabels) numLabels = 0;
948     PetscCall(PetscOptionsGetStringArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
949     if (!useColors) {
950       numColors = 3;
951       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
952     }
953     PetscCall(PetscOptionsGetStringArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
954     if (!useColors) {
955       numLColors = 4;
956       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
957     }
958     PetscCall(PetscOptionsGetString(((PetscObject) viewer)->options, ((PetscObject) viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
959     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
960     PetscCall(PetscOptionsGetBool(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
961     PetscCheck(!flg || !plotEdges || depth >= dim,PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Mesh must be interpolated");
962     if (depth < dim) plotEdges = PETSC_FALSE;
963     PetscCall(PetscOptionsGetBool(((PetscObject) viewer)->options, ((PetscObject) viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
964 
965     /* filter points with labelvalue != labeldefaultvalue */
966     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
967     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
968     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
969     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
970     if (lflg) {
971       DMLabel lbl;
972 
973       PetscCall(DMGetLabel(dm, lname, &lbl));
974       if (lbl) {
975         PetscInt val, defval;
976 
977         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
978         PetscCall(PetscBTCreate(pEnd-pStart, &wp));
979         for (c = pStart;  c < pEnd; c++) {
980           PetscInt *closure = NULL;
981           PetscInt  closureSize;
982 
983           PetscCall(DMLabelGetValue(lbl, c, &val));
984           if (val == defval) continue;
985 
986           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
987           for (p = 0; p < closureSize*2; p += 2) {
988             PetscCall(PetscBTSet(wp, closure[p] - pStart));
989           }
990           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
991         }
992       }
993     }
994 
995     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
996     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
997     PetscCall(PetscObjectGetName((PetscObject) dm, &name));
998     PetscCall(PetscViewerASCIIPrintf(viewer, "\
999 \\documentclass[tikz]{standalone}\n\n\
1000 \\usepackage{pgflibraryshapes}\n\
1001 \\usetikzlibrary{backgrounds}\n\
1002 \\usetikzlibrary{arrows}\n\
1003 \\begin{document}\n"));
1004     if (size > 1) {
1005       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1006       for (p = 0; p < size; ++p) {
1007         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size-1) ? ", and " :  ", "));
1008         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p%numColors], p));
1009       }
1010       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1011     }
1012     if (drawHasse) {
1013       PetscInt maxStratum = PetscMax(vEnd-vStart, PetscMax(eEnd-eStart, cEnd-cStart));
1014 
1015       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1016       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd-1));
1017       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd-vStart));
1018       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum-(vEnd-vStart))/2.));
1019       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1020       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd-1));
1021       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum-(eEnd-eStart))/2.));
1022       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd-eStart));
1023       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1024       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd-1));
1025       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd-cStart));
1026       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum-(cEnd-cStart))/2.));
1027     }
1028     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double) tikzscale));
1029 
1030     /* Plot vertices */
1031     PetscCall(VecGetArray(coordinates, &coords));
1032     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1033     for (v = vStart; v < vEnd; ++v) {
1034       PetscInt  off, dof, d;
1035       PetscBool isLabeled = PETSC_FALSE;
1036 
1037       if (wp && !PetscBTLookup(wp,v - pStart)) continue;
1038       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1039       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1040       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1041       PetscCheck(dof <= 3,PETSC_COMM_SELF,PETSC_ERR_PLIB,"coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3",v,dof);
1042       for (d = 0; d < dof; ++d) {
1043         tcoords[d] = (double) (scale*PetscRealPart(coords[off+d]));
1044         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1045       }
1046       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1047       if (dim == 3) {PetscReal tmp = tcoords[1]; tcoords[1] = tcoords[2]; tcoords[2] = -tmp;}
1048       for (d = 0; d < dof; ++d) {
1049         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1050         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double) tcoords[d]));
1051       }
1052       if (drawHasse) color = colors[0%numColors];
1053       else           color = colors[rank%numColors];
1054       for (l = 0; l < numLabels; ++l) {
1055         PetscInt val;
1056         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1057         if (val >= 0) {color = lcolors[l%numLColors]; isLabeled = PETSC_TRUE; break;}
1058       }
1059       if (drawNumbers[0]) {
1060         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1061       } else if (drawColors[0]) {
1062         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1063       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1064     }
1065     PetscCall(VecRestoreArray(coordinates, &coords));
1066     PetscCall(PetscViewerFlush(viewer));
1067     /* Plot edges */
1068     if (plotEdges) {
1069       PetscCall(VecGetArray(coordinates, &coords));
1070       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1071       for (e = eStart; e < eEnd; ++e) {
1072         const PetscInt *cone;
1073         PetscInt        coneSize, offA, offB, dof, d;
1074 
1075         if (wp && !PetscBTLookup(wp,e - pStart)) continue;
1076         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1077         PetscCheck(coneSize == 2,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1078         PetscCall(DMPlexGetCone(dm, e, &cone));
1079         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1080         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1081         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1082         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1083         for (d = 0; d < dof; ++d) {
1084           tcoords[d] = (double) (0.5*scale*PetscRealPart(coords[offA+d]+coords[offB+d]));
1085           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1086         }
1087         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1088         if (dim == 3) {PetscReal tmp = tcoords[1]; tcoords[1] = tcoords[2]; tcoords[2] = -tmp;}
1089         for (d = 0; d < dof; ++d) {
1090           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1091           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1092         }
1093         if (drawHasse) color = colors[1%numColors];
1094         else           color = colors[rank%numColors];
1095         for (l = 0; l < numLabels; ++l) {
1096           PetscInt val;
1097           PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1098           if (val >= 0) {color = lcolors[l%numLColors]; break;}
1099         }
1100         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1101       }
1102       PetscCall(VecRestoreArray(coordinates, &coords));
1103       PetscCall(PetscViewerFlush(viewer));
1104       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1105     }
1106     /* Plot cells */
1107     if (dim == 3 || !drawNumbers[1]) {
1108       for (e = eStart; e < eEnd; ++e) {
1109         const PetscInt *cone;
1110 
1111         if (wp && !PetscBTLookup(wp,e - pStart)) continue;
1112         color = colors[rank%numColors];
1113         for (l = 0; l < numLabels; ++l) {
1114           PetscInt val;
1115           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1116           if (val >= 0) {color = lcolors[l%numLColors]; break;}
1117         }
1118         PetscCall(DMPlexGetCone(dm, e, &cone));
1119         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1120       }
1121     } else {
1122        DMPolytopeType ct;
1123 
1124       /* Drawing a 2D polygon */
1125       for (c = cStart; c < cEnd; ++c) {
1126         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1127         PetscCall(DMPlexGetCellType(dm, c, &ct));
1128         if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR ||
1129             ct == DM_POLYTOPE_TRI_PRISM_TENSOR ||
1130             ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
1131           const PetscInt *cone;
1132           PetscInt        coneSize, e;
1133 
1134           PetscCall(DMPlexGetCone(dm, c, &cone));
1135           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1136           for (e = 0; e < coneSize; ++e) {
1137             const PetscInt *econe;
1138 
1139             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1140             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", colors[rank%numColors], econe[0], rank, cone[e], rank, econe[1], rank));
1141           }
1142         } else {
1143           PetscInt *closure = NULL;
1144           PetscInt  closureSize, Nv = 0, v;
1145 
1146           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1147           for (p = 0; p < closureSize*2; p += 2) {
1148             const PetscInt point = closure[p];
1149 
1150             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1151           }
1152           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank%numColors]));
1153           for (v = 0; v <= Nv; ++v) {
1154             const PetscInt vertex = closure[v%Nv];
1155 
1156             if (v > 0) {
1157               if (plotEdges) {
1158                 const PetscInt *edge;
1159                 PetscInt        endpoints[2], ne;
1160 
1161                 endpoints[0] = closure[v-1]; endpoints[1] = vertex;
1162                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1163                 PetscCheck(ne == 1,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1164                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1165                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1166               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1167             }
1168             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1169           }
1170           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1171           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1172         }
1173       }
1174     }
1175     PetscCall(VecGetArray(coordinates, &coords));
1176     for (c = cStart; c < cEnd; ++c) {
1177       double    ccoords[3] = {0.0, 0.0, 0.0};
1178       PetscBool isLabeled  = PETSC_FALSE;
1179       PetscInt *closure    = NULL;
1180       PetscInt  closureSize, dof, d, n = 0;
1181 
1182       if (wp && !PetscBTLookup(wp,c - pStart)) continue;
1183       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1184       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1185       for (p = 0; p < closureSize*2; p += 2) {
1186         const PetscInt point = closure[p];
1187         PetscInt       off;
1188 
1189         if ((point < vStart) || (point >= vEnd)) continue;
1190         PetscCall(PetscSectionGetDof(coordSection, point, &dof));
1191         PetscCall(PetscSectionGetOffset(coordSection, point, &off));
1192         for (d = 0; d < dof; ++d) {
1193           tcoords[d] = (double) (scale*PetscRealPart(coords[off+d]));
1194           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1195         }
1196         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1197         if (dof == 3) {PetscReal tmp = tcoords[1]; tcoords[1] = tcoords[2]; tcoords[2] = -tmp;}
1198         for (d = 0; d < dof; ++d) {ccoords[d] += tcoords[d];}
1199         ++n;
1200       }
1201       for (d = 0; d < dof; ++d) {ccoords[d] /= n;}
1202       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1203       for (d = 0; d < dof; ++d) {
1204         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1205         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double) ccoords[d]));
1206       }
1207       if (drawHasse) color = colors[depth%numColors];
1208       else           color = colors[rank%numColors];
1209       for (l = 0; l < numLabels; ++l) {
1210         PetscInt val;
1211         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1212         if (val >= 0) {color = lcolors[l%numLColors]; isLabeled = PETSC_TRUE; break;}
1213       }
1214       if (drawNumbers[dim]) {
1215         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1216       } else if (drawColors[dim]) {
1217         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1218       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1219     }
1220     PetscCall(VecRestoreArray(coordinates, &coords));
1221     if (drawHasse) {
1222       color = colors[depth%numColors];
1223       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1224       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1225       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1226       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1227       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1228 
1229       color = colors[1%numColors];
1230       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1231       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1232       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1233       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1234       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1235 
1236       color = colors[0%numColors];
1237       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1238       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1239       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1240       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1241       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1242 
1243       for (p = pStart; p < pEnd; ++p) {
1244         const PetscInt *cone;
1245         PetscInt        coneSize, cp;
1246 
1247         PetscCall(DMPlexGetCone(dm, p, &cone));
1248         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1249         for (cp = 0; cp < coneSize; ++cp) {
1250           PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1251         }
1252       }
1253     }
1254     PetscCall(PetscViewerFlush(viewer));
1255     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1256     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1257     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1258     for (l = 0; l < numLabels;  ++l) PetscCall(PetscFree(names[l]));
1259     for (c = 0; c < numColors;  ++c) PetscCall(PetscFree(colors[c]));
1260     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1261     PetscCall(PetscFree3(names, colors, lcolors));
1262     PetscCall(PetscBTDestroy(&wp));
1263   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1264     Vec                    cown,acown;
1265     VecScatter             sct;
1266     ISLocalToGlobalMapping g2l;
1267     IS                     gid,acis;
1268     MPI_Comm               comm,ncomm = MPI_COMM_NULL;
1269     MPI_Group              ggroup,ngroup;
1270     PetscScalar            *array,nid;
1271     const PetscInt         *idxs;
1272     PetscInt               *idxs2,*start,*adjacency,*work;
1273     PetscInt64             lm[3],gm[3];
1274     PetscInt               i,c,cStart,cEnd,cum,numVertices,ect,ectn,cellHeight;
1275     PetscMPIInt            d1,d2,rank;
1276 
1277     PetscCall(PetscObjectGetComm((PetscObject)dm,&comm));
1278     PetscCallMPI(MPI_Comm_rank(comm,&rank));
1279 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1280     PetscCallMPI(MPI_Comm_split_type(comm,MPI_COMM_TYPE_SHARED,rank,MPI_INFO_NULL,&ncomm));
1281 #endif
1282     if (ncomm != MPI_COMM_NULL) {
1283       PetscCallMPI(MPI_Comm_group(comm,&ggroup));
1284       PetscCallMPI(MPI_Comm_group(ncomm,&ngroup));
1285       d1   = 0;
1286       PetscCallMPI(MPI_Group_translate_ranks(ngroup,1,&d1,ggroup,&d2));
1287       nid  = d2;
1288       PetscCallMPI(MPI_Group_free(&ggroup));
1289       PetscCallMPI(MPI_Group_free(&ngroup));
1290       PetscCallMPI(MPI_Comm_free(&ncomm));
1291     } else nid = 0.0;
1292 
1293     /* Get connectivity */
1294     PetscCall(DMPlexGetVTKCellHeight(dm,&cellHeight));
1295     PetscCall(DMPlexCreatePartitionerGraph(dm,cellHeight,&numVertices,&start,&adjacency,&gid));
1296 
1297     /* filter overlapped local cells */
1298     PetscCall(DMPlexGetHeightStratum(dm,cellHeight,&cStart,&cEnd));
1299     PetscCall(ISGetIndices(gid,&idxs));
1300     PetscCall(ISGetLocalSize(gid,&cum));
1301     PetscCall(PetscMalloc1(cum,&idxs2));
1302     for (c = cStart, cum = 0; c < cEnd; c++) {
1303       if (idxs[c-cStart] < 0) continue;
1304       idxs2[cum++] = idxs[c-cStart];
1305     }
1306     PetscCall(ISRestoreIndices(gid,&idxs));
1307     PetscCheck(numVertices == cum,PETSC_COMM_SELF,PETSC_ERR_PLIB,"Unexpected %" PetscInt_FMT " != %" PetscInt_FMT,numVertices,cum);
1308     PetscCall(ISDestroy(&gid));
1309     PetscCall(ISCreateGeneral(comm,numVertices,idxs2,PETSC_OWN_POINTER,&gid));
1310 
1311     /* support for node-aware cell locality */
1312     PetscCall(ISCreateGeneral(comm,start[numVertices],adjacency,PETSC_USE_POINTER,&acis));
1313     PetscCall(VecCreateSeq(PETSC_COMM_SELF,start[numVertices],&acown));
1314     PetscCall(VecCreateMPI(comm,numVertices,PETSC_DECIDE,&cown));
1315     PetscCall(VecGetArray(cown,&array));
1316     for (c = 0; c < numVertices; c++) array[c] = nid;
1317     PetscCall(VecRestoreArray(cown,&array));
1318     PetscCall(VecScatterCreate(cown,acis,acown,NULL,&sct));
1319     PetscCall(VecScatterBegin(sct,cown,acown,INSERT_VALUES,SCATTER_FORWARD));
1320     PetscCall(VecScatterEnd(sct,cown,acown,INSERT_VALUES,SCATTER_FORWARD));
1321     PetscCall(ISDestroy(&acis));
1322     PetscCall(VecScatterDestroy(&sct));
1323     PetscCall(VecDestroy(&cown));
1324 
1325     /* compute edgeCut */
1326     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum,start[c+1]-start[c]);
1327     PetscCall(PetscMalloc1(cum,&work));
1328     PetscCall(ISLocalToGlobalMappingCreateIS(gid,&g2l));
1329     PetscCall(ISLocalToGlobalMappingSetType(g2l,ISLOCALTOGLOBALMAPPINGHASH));
1330     PetscCall(ISDestroy(&gid));
1331     PetscCall(VecGetArray(acown,&array));
1332     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1333       PetscInt totl;
1334 
1335       totl = start[c+1]-start[c];
1336       PetscCall(ISGlobalToLocalMappingApply(g2l,IS_GTOLM_MASK,totl,adjacency+start[c],NULL,work));
1337       for (i = 0; i < totl; i++) {
1338         if (work[i] < 0) {
1339           ect  += 1;
1340           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1341         }
1342       }
1343     }
1344     PetscCall(PetscFree(work));
1345     PetscCall(VecRestoreArray(acown,&array));
1346     lm[0] = numVertices > 0 ?  numVertices : PETSC_MAX_INT;
1347     lm[1] = -numVertices;
1348     PetscCall(MPIU_Allreduce(lm,gm,2,MPIU_INT64,MPI_MIN,comm));
1349     PetscCall(PetscViewerASCIIPrintf(viewer,"  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT,-((double)gm[1])/((double)gm[0]),-(PetscInt)gm[1],(PetscInt)gm[0]));
1350     lm[0] = ect; /* edgeCut */
1351     lm[1] = ectn; /* node-aware edgeCut */
1352     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1353     PetscCall(MPIU_Allreduce(lm,gm,3,MPIU_INT64,MPI_SUM,comm));
1354     PetscCall(PetscViewerASCIIPrintf(viewer,", empty %" PetscInt_FMT ")\n",(PetscInt)gm[2]));
1355 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1356     PetscCall(PetscViewerASCIIPrintf(viewer,"  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n",(PetscInt)(gm[0]/2),gm[0] ? ((double)(gm[1]))/((double)gm[0]) : 1.));
1357 #else
1358     PetscCall(PetscViewerASCIIPrintf(viewer,"  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n",(PetscInt)(gm[0]/2),0.0));
1359 #endif
1360     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1361     PetscCall(PetscFree(start));
1362     PetscCall(PetscFree(adjacency));
1363     PetscCall(VecDestroy(&acown));
1364   } else {
1365     const char    *name;
1366     PetscInt      *sizes, *hybsizes, *ghostsizes;
1367     PetscInt       locDepth, depth, cellHeight, dim, d;
1368     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1369     PetscInt       numLabels, l, maxSize = 17;
1370     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1371     MPI_Comm       comm;
1372     PetscMPIInt    size, rank;
1373 
1374     PetscCall(PetscObjectGetComm((PetscObject) dm, &comm));
1375     PetscCallMPI(MPI_Comm_size(comm, &size));
1376     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1377     PetscCall(DMGetDimension(dm, &dim));
1378     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1379     PetscCall(PetscObjectGetName((PetscObject) dm, &name));
1380     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1381     else      PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1382     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1383     PetscCall(DMPlexGetDepth(dm, &locDepth));
1384     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1385     PetscCall(DMPlexGetGhostCellStratum(dm, &gcStart, &gcEnd));
1386     gcNum = gcEnd - gcStart;
1387     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1388     else                PetscCall(PetscCalloc3(3,    &sizes, 3,    &hybsizes, 3,    &ghostsizes));
1389     for (d = 0; d <= depth; d++) {
1390       PetscInt Nc[2] = {0, 0}, ict;
1391 
1392       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1393       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1394       ict  = ct0;
1395       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1396       ct0  = (DMPolytopeType) ict;
1397       for (p = pStart; p < pEnd; ++p) {
1398         DMPolytopeType ct;
1399 
1400         PetscCall(DMPlexGetCellType(dm, p, &ct));
1401         if (ct == ct0) ++Nc[0];
1402         else           ++Nc[1];
1403       }
1404       if (size < maxSize) {
1405         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes,    1, MPIU_INT, 0, comm));
1406         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1407         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1408         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1409         for (p = 0; p < size; ++p) {
1410           if (rank == 0) {
1411             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p]+hybsizes[p]));
1412             if (hybsizes[p]   > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1413             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1414           }
1415         }
1416       } else {
1417         PetscInt locMinMax[2];
1418 
1419         locMinMax[0] = Nc[0]+Nc[1]; locMinMax[1] = Nc[0]+Nc[1];
1420         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1421         locMinMax[0] = Nc[1]; locMinMax[1] = Nc[1];
1422         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1423         if (d == depth) {
1424           locMinMax[0] = gcNum; locMinMax[1] = gcNum;
1425           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1426         }
1427         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1428         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1429         if (hybsizes[0]   > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1430         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1431       }
1432       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1433     }
1434     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1435     {
1436       const PetscReal *maxCell;
1437       const PetscReal *L;
1438       PetscBool        localized;
1439 
1440       PetscCall(DMGetPeriodicity(dm, &maxCell, &L));
1441       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1442       if (L || localized) {
1443         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1444         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1445         if (L) {
1446           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1447           for (d = 0; d < dim; ++d) {
1448             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1449             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1450           }
1451           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1452         }
1453         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1454         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1455       }
1456     }
1457     PetscCall(DMGetNumLabels(dm, &numLabels));
1458     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1459     for (l = 0; l < numLabels; ++l) {
1460       DMLabel         label;
1461       const char     *name;
1462       IS              valueIS;
1463       const PetscInt *values;
1464       PetscInt        numValues, v;
1465 
1466       PetscCall(DMGetLabelName(dm, l, &name));
1467       PetscCall(DMGetLabel(dm, name, &label));
1468       PetscCall(DMLabelGetNumValues(label, &numValues));
1469       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1470       PetscCall(DMLabelGetValueIS(label, &valueIS));
1471       PetscCall(ISGetIndices(valueIS, &values));
1472       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1473       for (v = 0; v < numValues; ++v) {
1474         PetscInt size;
1475 
1476         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1477         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1478         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1479       }
1480       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1481       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1482       PetscCall(ISRestoreIndices(valueIS, &values));
1483       PetscCall(ISDestroy(&valueIS));
1484     }
1485     {
1486       char    **labelNames;
1487       PetscInt  Nl = numLabels;
1488       PetscBool flg;
1489 
1490       PetscCall(PetscMalloc1(Nl, &labelNames));
1491       PetscCall(PetscOptionsGetStringArray(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1492       for (l = 0; l < Nl; ++l) {
1493         DMLabel label;
1494 
1495         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1496         if (flg) {
1497           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1498           PetscCall(DMLabelView(label, viewer));
1499         }
1500         PetscCall(PetscFree(labelNames[l]));
1501       }
1502       PetscCall(PetscFree(labelNames));
1503     }
1504     /* If no fields are specified, people do not want to see adjacency */
1505     if (dm->Nf) {
1506       PetscInt f;
1507 
1508       for (f = 0; f < dm->Nf; ++f) {
1509         const char *name;
1510 
1511         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1512         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1513         PetscCall(PetscViewerASCIIPushTab(viewer));
1514         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1515         if (dm->fields[f].adjacency[0]) {
1516           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1517           else                            PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1518         } else {
1519           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1520           else                            PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1521         }
1522         PetscCall(PetscViewerASCIIPopTab(viewer));
1523       }
1524     }
1525     PetscCall(DMGetCoarseDM(dm, &cdm));
1526     if (cdm) {
1527       PetscCall(PetscViewerASCIIPushTab(viewer));
1528       PetscCall(DMPlexView_Ascii(cdm, viewer));
1529       PetscCall(PetscViewerASCIIPopTab(viewer));
1530     }
1531   }
1532   PetscFunctionReturn(0);
1533 }
1534 
1535 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1536 {
1537   DMPolytopeType ct;
1538   PetscMPIInt    rank;
1539   PetscInt       cdim;
1540 
1541   PetscFunctionBegin;
1542   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject) dm), &rank));
1543   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1544   PetscCall(DMGetCoordinateDim(dm, &cdim));
1545   switch (ct) {
1546   case DM_POLYTOPE_SEGMENT:
1547   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1548     switch (cdim) {
1549     case 1:
1550     {
1551       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1552       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1553 
1554       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y,    PetscRealPart(coords[1]), y,    PETSC_DRAW_BLACK));
1555       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y+dy, PetscRealPart(coords[0]), y-dy, PETSC_DRAW_BLACK));
1556       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y+dy, PetscRealPart(coords[1]), y-dy, PETSC_DRAW_BLACK));
1557     }
1558     break;
1559     case 2:
1560     {
1561       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1562       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1563       const PetscReal l  = 0.1/PetscSqrtReal(dx*dx + dy*dy);
1564 
1565       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1566       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0])+l*dx, PetscRealPart(coords[1])+l*dy, PetscRealPart(coords[0])-l*dx, PetscRealPart(coords[1])-l*dy, PETSC_DRAW_BLACK));
1567       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2])+l*dx, PetscRealPart(coords[3])+l*dy, PetscRealPart(coords[2])-l*dx, PetscRealPart(coords[3])-l*dy, PETSC_DRAW_BLACK));
1568     }
1569     break;
1570     default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1571     }
1572     break;
1573   case DM_POLYTOPE_TRIANGLE:
1574     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]),
1575                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1576                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1577                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2));
1578     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1579     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1580     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1581     break;
1582   case DM_POLYTOPE_QUADRILATERAL:
1583     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]),
1584                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1585                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1586                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2));
1587     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]),
1588                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1589                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1590                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2));
1591     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1592     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1593     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1594     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1595     break;
1596   default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1597   }
1598   PetscFunctionReturn(0);
1599 }
1600 
1601 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1602 {
1603   DMPolytopeType ct;
1604   PetscReal      centroid[2] = {0., 0.};
1605   PetscMPIInt    rank;
1606   PetscInt       fillColor, v, e, d;
1607 
1608   PetscFunctionBegin;
1609   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject) dm), &rank));
1610   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1611   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2;
1612   switch (ct) {
1613   case DM_POLYTOPE_TRIANGLE:
1614     {
1615       PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1616 
1617       for (v = 0; v < 3; ++v) {centroid[0] += PetscRealPart(coords[v*2+0])/3.;centroid[1] += PetscRealPart(coords[v*2+1])/3.;}
1618       for (e = 0; e < 3; ++e) {
1619         refCoords[0] = refVertices[e*2+0];
1620         refCoords[1] = refVertices[e*2+1];
1621         for (d = 1; d <= edgeDiv; ++d) {
1622           refCoords[d*2+0] = refCoords[0] + (refVertices[(e+1)%3 * 2 + 0] - refCoords[0])*d/edgeDiv;
1623           refCoords[d*2+1] = refCoords[1] + (refVertices[(e+1)%3 * 2 + 1] - refCoords[1])*d/edgeDiv;
1624         }
1625         PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv+1, refCoords, edgeCoords));
1626         for (d = 0; d < edgeDiv; ++d) {
1627           PetscCall(PetscDrawTriangle(draw, centroid[0], centroid[1], edgeCoords[d*2+0], edgeCoords[d*2+1], edgeCoords[(d+1)*2+0], edgeCoords[(d+1)*2+1], fillColor, fillColor, fillColor));
1628           PetscCall(PetscDrawLine(draw, edgeCoords[d*2+0], edgeCoords[d*2+1], edgeCoords[(d+1)*2+0], edgeCoords[(d+1)*2+1], PETSC_DRAW_BLACK));
1629         }
1630       }
1631     }
1632     break;
1633   default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1634   }
1635   PetscFunctionReturn(0);
1636 }
1637 
1638 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1639 {
1640   PetscDraw          draw;
1641   DM                 cdm;
1642   PetscSection       coordSection;
1643   Vec                coordinates;
1644   const PetscScalar *coords;
1645   PetscReal          xyl[2],xyr[2],bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
1646   PetscReal         *refCoords, *edgeCoords;
1647   PetscBool          isnull, drawAffine = PETSC_TRUE;
1648   PetscInt           dim, vStart, vEnd, cStart, cEnd, c, N, edgeDiv = 4;
1649 
1650   PetscFunctionBegin;
1651   PetscCall(DMGetCoordinateDim(dm, &dim));
1652   PetscCheck(dim <= 2,PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1653   PetscCall(PetscOptionsGetBool(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1654   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv+1)*dim, &refCoords, (edgeDiv+1)*dim, &edgeCoords));
1655   PetscCall(DMGetCoordinateDM(dm, &cdm));
1656   PetscCall(DMGetLocalSection(cdm, &coordSection));
1657   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1658   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1659   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1660 
1661   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1662   PetscCall(PetscDrawIsNull(draw, &isnull));
1663   if (isnull) PetscFunctionReturn(0);
1664   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1665 
1666   PetscCall(VecGetLocalSize(coordinates, &N));
1667   PetscCall(VecGetArrayRead(coordinates, &coords));
1668   for (c = 0; c < N; c += dim) {
1669     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));   bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
1670     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c+1])); bound[3] = PetscMax(bound[3], PetscRealPart(coords[c+1]));
1671   }
1672   PetscCall(VecRestoreArrayRead(coordinates, &coords));
1673   PetscCall(MPIU_Allreduce(&bound[0],xyl,2,MPIU_REAL,MPIU_MIN,PetscObjectComm((PetscObject)dm)));
1674   PetscCall(MPIU_Allreduce(&bound[2],xyr,2,MPIU_REAL,MPIU_MAX,PetscObjectComm((PetscObject)dm)));
1675   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1676   PetscCall(PetscDrawClear(draw));
1677 
1678   for (c = cStart; c < cEnd; ++c) {
1679     PetscScalar *coords = NULL;
1680     PetscInt     numCoords;
1681 
1682     PetscCall(DMPlexVecGetClosureAtDepth_Internal(dm, coordSection, coordinates, c, 0, &numCoords, &coords));
1683     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1684     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1685     PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
1686   }
1687   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1688   PetscCall(PetscDrawFlush(draw));
1689   PetscCall(PetscDrawPause(draw));
1690   PetscCall(PetscDrawSave(draw));
1691   PetscFunctionReturn(0);
1692 }
1693 
1694 #if defined(PETSC_HAVE_EXODUSII)
1695 #include <exodusII.h>
1696 #include <petscviewerexodusii.h>
1697 #endif
1698 
1699 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1700 {
1701   PetscBool      iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus;
1702   char           name[PETSC_MAX_PATH_LEN];
1703 
1704   PetscFunctionBegin;
1705   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1706   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1707   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERASCII,    &iascii));
1708   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,      &isvtk));
1709   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,     &ishdf5));
1710   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERDRAW,     &isdraw));
1711   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERGLVIS,    &isglvis));
1712   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWEREXODUSII, &isexodus));
1713   if (iascii) {
1714     PetscViewerFormat format;
1715     PetscCall(PetscViewerGetFormat(viewer, &format));
1716     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1717     else PetscCall(DMPlexView_Ascii(dm, viewer));
1718   } else if (ishdf5) {
1719 #if defined(PETSC_HAVE_HDF5)
1720     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1721 #else
1722     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1723 #endif
1724   } else if (isvtk) {
1725     PetscCall(DMPlexVTKWriteAll((PetscObject) dm,viewer));
1726   } else if (isdraw) {
1727     PetscCall(DMPlexView_Draw(dm, viewer));
1728   } else if (isglvis) {
1729     PetscCall(DMPlexView_GLVis(dm, viewer));
1730 #if defined(PETSC_HAVE_EXODUSII)
1731   } else if (isexodus) {
1732 /*
1733       exodusII requires that all sets be part of exactly one cell set.
1734       If the dm does not have a "Cell Sets" label defined, we create one
1735       with ID 1, containig all cells.
1736       Note that if the Cell Sets label is defined but does not cover all cells,
1737       we may still have a problem. This should probably be checked here or in the viewer;
1738     */
1739     PetscInt numCS;
1740     PetscCall(DMGetLabelSize(dm,"Cell Sets",&numCS));
1741     if (!numCS) {
1742       PetscInt cStart, cEnd, c;
1743       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1744       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1745       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1746     }
1747     PetscCall(DMView_PlexExodusII(dm, viewer));
1748 #endif
1749   } else SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1750 
1751   /* Optionally view the partition */
1752   PetscCall(PetscOptionsHasName(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_partition_view", &flg));
1753   if (flg) {
1754     Vec ranks;
1755     PetscCall(DMPlexCreateRankField(dm, &ranks));
1756     PetscCall(VecView(ranks, viewer));
1757     PetscCall(VecDestroy(&ranks));
1758   }
1759   /* Optionally view a label */
1760   PetscCall(PetscOptionsGetString(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1761   if (flg) {
1762     DMLabel label;
1763     Vec     val;
1764 
1765     PetscCall(DMGetLabel(dm, name, &label));
1766     PetscCheck(label,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1767     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1768     PetscCall(VecView(val, viewer));
1769     PetscCall(VecDestroy(&val));
1770   }
1771   PetscFunctionReturn(0);
1772 }
1773 
1774 /*@
1775   DMPlexTopologyView - Saves a DMPlex topology into a file
1776 
1777   Collective on DM
1778 
1779   Input Parameters:
1780 + dm     - The DM whose topology is to be saved
1781 - viewer - The PetscViewer for saving
1782 
1783   Level: advanced
1784 
1785 .seealso: `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`
1786 @*/
1787 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
1788 {
1789   PetscBool      ishdf5;
1790 
1791   PetscFunctionBegin;
1792   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1793   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1794   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
1795   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView,viewer,0,0,0));
1796   if (ishdf5) {
1797 #if defined(PETSC_HAVE_HDF5)
1798     PetscViewerFormat format;
1799     PetscCall(PetscViewerGetFormat(viewer, &format));
1800     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1801       IS globalPointNumbering;
1802 
1803       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1804       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
1805       PetscCall(ISDestroy(&globalPointNumbering));
1806     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1807 #else
1808     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1809 #endif
1810   }
1811   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView,viewer,0,0,0));
1812   PetscFunctionReturn(0);
1813 }
1814 
1815 /*@
1816   DMPlexCoordinatesView - Saves DMPlex coordinates into a file
1817 
1818   Collective on DM
1819 
1820   Input Parameters:
1821 + dm     - The DM whose coordinates are to be saved
1822 - viewer - The PetscViewer for saving
1823 
1824   Level: advanced
1825 
1826 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`
1827 @*/
1828 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
1829 {
1830   PetscBool      ishdf5;
1831 
1832   PetscFunctionBegin;
1833   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1834   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1835   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
1836   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView,viewer,0,0,0));
1837   if (ishdf5) {
1838 #if defined(PETSC_HAVE_HDF5)
1839     PetscViewerFormat format;
1840     PetscCall(PetscViewerGetFormat(viewer, &format));
1841     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1842       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
1843     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1844 #else
1845     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1846 #endif
1847   }
1848   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView,viewer,0,0,0));
1849   PetscFunctionReturn(0);
1850 }
1851 
1852 /*@
1853   DMPlexLabelsView - Saves DMPlex labels into a file
1854 
1855   Collective on DM
1856 
1857   Input Parameters:
1858 + dm     - The DM whose labels are to be saved
1859 - viewer - The PetscViewer for saving
1860 
1861   Level: advanced
1862 
1863 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`
1864 @*/
1865 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
1866 {
1867   PetscBool      ishdf5;
1868 
1869   PetscFunctionBegin;
1870   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1871   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1872   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
1873   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView,viewer,0,0,0));
1874   if (ishdf5) {
1875 #if defined(PETSC_HAVE_HDF5)
1876     IS                globalPointNumbering;
1877     PetscViewerFormat format;
1878 
1879     PetscCall(PetscViewerGetFormat(viewer, &format));
1880     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1881       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1882       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
1883       PetscCall(ISDestroy(&globalPointNumbering));
1884     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1885 #else
1886     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1887 #endif
1888   }
1889   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView,viewer,0,0,0));
1890   PetscFunctionReturn(0);
1891 }
1892 
1893 /*@
1894   DMPlexSectionView - Saves a section associated with a DMPlex
1895 
1896   Collective on DM
1897 
1898   Input Parameters:
1899 + dm         - The DM that contains the topology on which the section to be saved is defined
1900 . viewer     - The PetscViewer for saving
1901 - sectiondm  - The DM that contains the section to be saved
1902 
1903   Level: advanced
1904 
1905   Notes:
1906   This function is a wrapper around PetscSectionView(); in addition to the raw section, it saves information that associates the section points to the topology (dm) points. When the topology (dm) and the section are later loaded with DMPlexTopologyLoad() and DMPlexSectionLoad(), respectively, this information is used to match section points with topology points.
1907 
1908   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
1909 
1910 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`
1911 @*/
1912 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
1913 {
1914   PetscBool      ishdf5;
1915 
1916   PetscFunctionBegin;
1917   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1918   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1919   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1920   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
1921   PetscCall(PetscLogEventBegin(DMPLEX_SectionView,viewer,0,0,0));
1922   if (ishdf5) {
1923 #if defined(PETSC_HAVE_HDF5)
1924     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
1925 #else
1926     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1927 #endif
1928   }
1929   PetscCall(PetscLogEventEnd(DMPLEX_SectionView,viewer,0,0,0));
1930   PetscFunctionReturn(0);
1931 }
1932 
1933 /*@
1934   DMPlexGlobalVectorView - Saves a global vector
1935 
1936   Collective on DM
1937 
1938   Input Parameters:
1939 + dm        - The DM that represents the topology
1940 . viewer    - The PetscViewer to save data with
1941 . sectiondm - The DM that contains the global section on which vec is defined
1942 - vec       - The global vector to be saved
1943 
1944   Level: advanced
1945 
1946   Notes:
1947   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
1948 
1949   Typical calling sequence
1950 $       DMCreate(PETSC_COMM_WORLD, &dm);
1951 $       DMSetType(dm, DMPLEX);
1952 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
1953 $       DMClone(dm, &sectiondm);
1954 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
1955 $       PetscSectionCreate(PETSC_COMM_WORLD, &section);
1956 $       DMPlexGetChart(sectiondm, &pStart, &pEnd);
1957 $       PetscSectionSetChart(section, pStart, pEnd);
1958 $       PetscSectionSetUp(section);
1959 $       DMSetLocalSection(sectiondm, section);
1960 $       PetscSectionDestroy(&section);
1961 $       DMGetGlobalVector(sectiondm, &vec);
1962 $       PetscObjectSetName((PetscObject)vec, "vec_name");
1963 $       DMPlexTopologyView(dm, viewer);
1964 $       DMPlexSectionView(dm, viewer, sectiondm);
1965 $       DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
1966 $       DMRestoreGlobalVector(sectiondm, &vec);
1967 $       DMDestroy(&sectiondm);
1968 $       DMDestroy(&dm);
1969 
1970 .seealso: `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
1971 @*/
1972 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
1973 {
1974   PetscBool       ishdf5;
1975 
1976   PetscFunctionBegin;
1977   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1978   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1979   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1980   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
1981   /* Check consistency */
1982   {
1983     PetscSection  section;
1984     PetscBool     includesConstraints;
1985     PetscInt      m, m1;
1986 
1987     PetscCall(VecGetLocalSize(vec, &m1));
1988     PetscCall(DMGetGlobalSection(sectiondm, &section));
1989     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
1990     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
1991     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
1992     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
1993   }
1994   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1995   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView,viewer,0,0,0));
1996   if (ishdf5) {
1997 #if defined(PETSC_HAVE_HDF5)
1998     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
1999 #else
2000     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2001 #endif
2002   }
2003   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView,viewer,0,0,0));
2004   PetscFunctionReturn(0);
2005 }
2006 
2007 /*@
2008   DMPlexLocalVectorView - Saves a local vector
2009 
2010   Collective on DM
2011 
2012   Input Parameters:
2013 + dm        - The DM that represents the topology
2014 . viewer    - The PetscViewer to save data with
2015 . sectiondm - The DM that contains the local section on which vec is defined; may be the same as dm
2016 - vec       - The local vector to be saved
2017 
2018   Level: advanced
2019 
2020   Notes:
2021   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2022 
2023   Typical calling sequence
2024 $       DMCreate(PETSC_COMM_WORLD, &dm);
2025 $       DMSetType(dm, DMPLEX);
2026 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2027 $       DMClone(dm, &sectiondm);
2028 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2029 $       PetscSectionCreate(PETSC_COMM_WORLD, &section);
2030 $       DMPlexGetChart(sectiondm, &pStart, &pEnd);
2031 $       PetscSectionSetChart(section, pStart, pEnd);
2032 $       PetscSectionSetUp(section);
2033 $       DMSetLocalSection(sectiondm, section);
2034 $       DMGetLocalVector(sectiondm, &vec);
2035 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2036 $       DMPlexTopologyView(dm, viewer);
2037 $       DMPlexSectionView(dm, viewer, sectiondm);
2038 $       DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2039 $       DMRestoreLocalVector(sectiondm, &vec);
2040 $       DMDestroy(&sectiondm);
2041 $       DMDestroy(&dm);
2042 
2043 .seealso: `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2044 @*/
2045 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2046 {
2047   PetscBool       ishdf5;
2048 
2049   PetscFunctionBegin;
2050   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2051   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2052   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2053   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2054   /* Check consistency */
2055   {
2056     PetscSection  section;
2057     PetscBool     includesConstraints;
2058     PetscInt      m, m1;
2059 
2060     PetscCall(VecGetLocalSize(vec, &m1));
2061     PetscCall(DMGetLocalSection(sectiondm, &section));
2062     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2063     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2064     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2065     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2066   }
2067   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2068   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView,viewer,0,0,0));
2069   if (ishdf5) {
2070 #if defined(PETSC_HAVE_HDF5)
2071     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2072 #else
2073     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2074 #endif
2075   }
2076   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView,viewer,0,0,0));
2077   PetscFunctionReturn(0);
2078 }
2079 
2080 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2081 {
2082   PetscBool      ishdf5;
2083 
2084   PetscFunctionBegin;
2085   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2086   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2087   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,   &ishdf5));
2088   if (ishdf5) {
2089 #if defined(PETSC_HAVE_HDF5)
2090     PetscViewerFormat format;
2091     PetscCall(PetscViewerGetFormat(viewer, &format));
2092     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2093       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2094     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2095       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2096     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2097     PetscFunctionReturn(0);
2098 #else
2099     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2100 #endif
2101   } else SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2102 }
2103 
2104 /*@
2105   DMPlexTopologyLoad - Loads a topology into a DMPlex
2106 
2107   Collective on DM
2108 
2109   Input Parameters:
2110 + dm     - The DM into which the topology is loaded
2111 - viewer - The PetscViewer for the saved topology
2112 
2113   Output Parameters:
2114 . globalToLocalPointSF - The PetscSF that pushes points in [0, N) to the associated points in the loaded plex, where N is the global number of points; NULL if unneeded
2115 
2116   Level: advanced
2117 
2118 .seealso: `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2119 @*/
2120 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2121 {
2122   PetscBool      ishdf5;
2123 
2124   PetscFunctionBegin;
2125   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2126   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2127   if (globalToLocalPointSF) PetscValidPointer(globalToLocalPointSF, 3);
2128   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
2129   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad,viewer,0,0,0));
2130   if (ishdf5) {
2131 #if defined(PETSC_HAVE_HDF5)
2132     PetscViewerFormat format;
2133     PetscCall(PetscViewerGetFormat(viewer, &format));
2134     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2135       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2136     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2137 #else
2138     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2139 #endif
2140   }
2141   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad,viewer,0,0,0));
2142   PetscFunctionReturn(0);
2143 }
2144 
2145 /*@
2146   DMPlexCoordinatesLoad - Loads coordinates into a DMPlex
2147 
2148   Collective on DM
2149 
2150   Input Parameters:
2151 + dm     - The DM into which the coordinates are loaded
2152 . viewer - The PetscViewer for the saved coordinates
2153 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2154 
2155   Level: advanced
2156 
2157 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2158 @*/
2159 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2160 {
2161   PetscBool      ishdf5;
2162 
2163   PetscFunctionBegin;
2164   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2165   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2166   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2167   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
2168   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad,viewer,0,0,0));
2169   if (ishdf5) {
2170 #if defined(PETSC_HAVE_HDF5)
2171     PetscViewerFormat format;
2172     PetscCall(PetscViewerGetFormat(viewer, &format));
2173     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2174       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2175     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2176 #else
2177     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2178 #endif
2179   }
2180   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad,viewer,0,0,0));
2181   PetscFunctionReturn(0);
2182 }
2183 
2184 /*@
2185   DMPlexLabelsLoad - Loads labels into a DMPlex
2186 
2187   Collective on DM
2188 
2189   Input Parameters:
2190 + dm     - The DM into which the labels are loaded
2191 . viewer - The PetscViewer for the saved labels
2192 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2193 
2194   Level: advanced
2195 
2196   Notes:
2197   The PetscSF argument must not be NULL if the DM is distributed, otherwise an error occurs.
2198 
2199 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2200 @*/
2201 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2202 {
2203   PetscBool      ishdf5;
2204 
2205   PetscFunctionBegin;
2206   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2207   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2208   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2209   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
2210   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad,viewer,0,0,0));
2211   if (ishdf5) {
2212 #if defined(PETSC_HAVE_HDF5)
2213     PetscViewerFormat format;
2214 
2215     PetscCall(PetscViewerGetFormat(viewer, &format));
2216     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2217       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2218     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2219 #else
2220     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2221 #endif
2222   }
2223   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad,viewer,0,0,0));
2224   PetscFunctionReturn(0);
2225 }
2226 
2227 /*@
2228   DMPlexSectionLoad - Loads section into a DMPlex
2229 
2230   Collective on DM
2231 
2232   Input Parameters:
2233 + dm          - The DM that represents the topology
2234 . viewer      - The PetscViewer that represents the on-disk section (sectionA)
2235 . sectiondm   - The DM into which the on-disk section (sectionA) is migrated
2236 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2237 
2238   Output Parameters
2239 + globalDofSF - The SF that migrates any on-disk Vec data associated with sectionA into a global Vec associated with the sectiondm's global section (NULL if not needed)
2240 - localDofSF  - The SF that migrates any on-disk Vec data associated with sectionA into a local Vec associated with the sectiondm's local section (NULL if not needed)
2241 
2242   Level: advanced
2243 
2244   Notes:
2245   This function is a wrapper around PetscSectionLoad(); it loads, in addition to the raw section, a list of global point numbers that associates each on-disk section point with a global point number in [0, NX), where NX is the number of topology points in dm. Noting that globalToLocalPointSF associates each topology point in dm with a global number in [0, NX), one can readily establish an association of the on-disk section points with the topology points.
2246 
2247   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2248 
2249   The output parameter, globalDofSF (localDofSF), can later be used with DMPlexGlobalVectorLoad() (DMPlexLocalVectorLoad()) to load on-disk vectors into global (local) vectors associated with sectiondm's global (local) section.
2250 
2251   Example using 2 processes:
2252 $  NX (number of points on dm): 4
2253 $  sectionA                   : the on-disk section
2254 $  vecA                       : a vector associated with sectionA
2255 $  sectionB                   : sectiondm's local section constructed in this function
2256 $  vecB (local)               : a vector associated with sectiondm's local section
2257 $  vecB (global)              : a vector associated with sectiondm's global section
2258 $
2259 $                                     rank 0    rank 1
2260 $  vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2261 $  sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2262 $  sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2263 $  sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2264 $  [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2265 $  sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2266 $  sectionB->atlasDof             :     1 0 1 | 1 3
2267 $  sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2268 $  vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2269 $  vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2270 $
2271 $  where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2272 
2273 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`
2274 @*/
2275 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2276 {
2277   PetscBool      ishdf5;
2278 
2279   PetscFunctionBegin;
2280   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2281   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2282   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2283   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2284   if (globalDofSF) PetscValidPointer(globalDofSF, 5);
2285   if (localDofSF) PetscValidPointer(localDofSF, 6);
2286   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
2287   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad,viewer,0,0,0));
2288   if (ishdf5) {
2289 #if defined(PETSC_HAVE_HDF5)
2290     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2291 #else
2292     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2293 #endif
2294   }
2295   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad,viewer,0,0,0));
2296   PetscFunctionReturn(0);
2297 }
2298 
2299 /*@
2300   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2301 
2302   Collective on DM
2303 
2304   Input Parameters:
2305 + dm        - The DM that represents the topology
2306 . viewer    - The PetscViewer that represents the on-disk vector data
2307 . sectiondm - The DM that contains the global section on which vec is defined
2308 . sf        - The SF that migrates the on-disk vector data into vec
2309 - vec       - The global vector to set values of
2310 
2311   Level: advanced
2312 
2313   Notes:
2314   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2315 
2316   Typical calling sequence
2317 $       DMCreate(PETSC_COMM_WORLD, &dm);
2318 $       DMSetType(dm, DMPLEX);
2319 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2320 $       DMPlexTopologyLoad(dm, viewer, &sfX);
2321 $       DMClone(dm, &sectiondm);
2322 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2323 $       DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2324 $       DMGetGlobalVector(sectiondm, &vec);
2325 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2326 $       DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2327 $       DMRestoreGlobalVector(sectiondm, &vec);
2328 $       PetscSFDestroy(&gsf);
2329 $       PetscSFDestroy(&sfX);
2330 $       DMDestroy(&sectiondm);
2331 $       DMDestroy(&dm);
2332 
2333 .seealso: `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`
2334 @*/
2335 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2336 {
2337   PetscBool       ishdf5;
2338 
2339   PetscFunctionBegin;
2340   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2341   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2342   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2343   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2344   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2345   /* Check consistency */
2346   {
2347     PetscSection  section;
2348     PetscBool     includesConstraints;
2349     PetscInt      m, m1;
2350 
2351     PetscCall(VecGetLocalSize(vec, &m1));
2352     PetscCall(DMGetGlobalSection(sectiondm, &section));
2353     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2354     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2355     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2356     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2357   }
2358   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
2359   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad,viewer,0,0,0));
2360   if (ishdf5) {
2361 #if defined(PETSC_HAVE_HDF5)
2362     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2363 #else
2364     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2365 #endif
2366   }
2367   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad,viewer,0,0,0));
2368   PetscFunctionReturn(0);
2369 }
2370 
2371 /*@
2372   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2373 
2374   Collective on DM
2375 
2376   Input Parameters:
2377 + dm        - The DM that represents the topology
2378 . viewer    - The PetscViewer that represents the on-disk vector data
2379 . sectiondm - The DM that contains the local section on which vec is defined
2380 . sf        - The SF that migrates the on-disk vector data into vec
2381 - vec       - The local vector to set values of
2382 
2383   Level: advanced
2384 
2385   Notes:
2386   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with PetscObjectSetName(). In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2387 
2388   Typical calling sequence
2389 $       DMCreate(PETSC_COMM_WORLD, &dm);
2390 $       DMSetType(dm, DMPLEX);
2391 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2392 $       DMPlexTopologyLoad(dm, viewer, &sfX);
2393 $       DMClone(dm, &sectiondm);
2394 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2395 $       DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2396 $       DMGetLocalVector(sectiondm, &vec);
2397 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2398 $       DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2399 $       DMRestoreLocalVector(sectiondm, &vec);
2400 $       PetscSFDestroy(&lsf);
2401 $       PetscSFDestroy(&sfX);
2402 $       DMDestroy(&sectiondm);
2403 $       DMDestroy(&dm);
2404 
2405 .seealso: `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`
2406 @*/
2407 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2408 {
2409   PetscBool       ishdf5;
2410 
2411   PetscFunctionBegin;
2412   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2413   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2414   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2415   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2416   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2417   /* Check consistency */
2418   {
2419     PetscSection  section;
2420     PetscBool     includesConstraints;
2421     PetscInt      m, m1;
2422 
2423     PetscCall(VecGetLocalSize(vec, &m1));
2424     PetscCall(DMGetLocalSection(sectiondm, &section));
2425     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2426     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2427     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2428     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2429   }
2430   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
2431   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad,viewer,0,0,0));
2432   if (ishdf5) {
2433 #if defined(PETSC_HAVE_HDF5)
2434     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2435 #else
2436     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2437 #endif
2438   }
2439   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad,viewer,0,0,0));
2440   PetscFunctionReturn(0);
2441 }
2442 
2443 PetscErrorCode DMDestroy_Plex(DM dm)
2444 {
2445   DM_Plex       *mesh = (DM_Plex*) dm->data;
2446 
2447   PetscFunctionBegin;
2448   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMSetUpGLVisViewer_C",NULL));
2449   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexInsertBoundaryValues_C", NULL));
2450   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMCreateNeumannOverlap_C", NULL));
2451   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMInterpolateSolution_C", NULL));
2452   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexInsertTimeDerviativeBoundaryValues_C", NULL));
2453   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexGetOverlap_C", NULL));
2454   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexDistributeGetDefault_C", NULL));
2455   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexDistributeSetDefault_C", NULL));
2456   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"MatComputeNeumannOverlap_C",NULL));
2457   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexReorderGetDefault_C", NULL));
2458   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexReorderSetDefault_C", NULL));
2459   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexGetOverlap_C",NULL));
2460   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexSetOverlap_C",NULL));
2461   if (--mesh->refct > 0) PetscFunctionReturn(0);
2462   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2463   PetscCall(PetscFree(mesh->cones));
2464   PetscCall(PetscFree(mesh->coneOrientations));
2465   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2466   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2467   PetscCall(PetscFree(mesh->supports));
2468   PetscCall(PetscFree(mesh->facesTmp));
2469   PetscCall(PetscFree(mesh->tetgenOpts));
2470   PetscCall(PetscFree(mesh->triangleOpts));
2471   PetscCall(PetscFree(mesh->transformType));
2472   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2473   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2474   PetscCall(ISDestroy(&mesh->subpointIS));
2475   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2476   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2477   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2478   PetscCall(ISDestroy(&mesh->anchorIS));
2479   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2480   PetscCall(PetscFree(mesh->parents));
2481   PetscCall(PetscFree(mesh->childIDs));
2482   PetscCall(PetscSectionDestroy(&mesh->childSection));
2483   PetscCall(PetscFree(mesh->children));
2484   PetscCall(DMDestroy(&mesh->referenceTree));
2485   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2486   PetscCall(PetscFree(mesh->neighbors));
2487   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2488   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2489   PetscCall(PetscFree(mesh));
2490   PetscFunctionReturn(0);
2491 }
2492 
2493 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2494 {
2495   PetscSection           sectionGlobal;
2496   PetscInt               bs = -1, mbs;
2497   PetscInt               localSize, localStart = 0;
2498   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2499   MatType                mtype;
2500   ISLocalToGlobalMapping ltog;
2501 
2502   PetscFunctionBegin;
2503   PetscCall(MatInitializePackage());
2504   mtype = dm->mattype;
2505   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2506   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2507   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2508   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject) dm)));
2509   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2510   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2511   PetscCall(MatSetType(*J, mtype));
2512   PetscCall(MatSetFromOptions(*J));
2513   PetscCall(MatGetBlockSize(*J, &mbs));
2514   if (mbs > 1) bs = mbs;
2515   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2516   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2517   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2518   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2519   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2520   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2521   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2522   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2523   if (!isShell) {
2524     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2525     PetscInt  *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2526     PetscInt  pStart, pEnd, p, dof, cdof;
2527 
2528     PetscCall(DMGetLocalToGlobalMapping(dm,&ltog));
2529 
2530     PetscCall(PetscCalloc1(localSize, &pblocks));
2531     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2532     for (p = pStart; p < pEnd; ++p) {
2533       PetscInt bdof, offset;
2534 
2535       PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2536       PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2537       PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2538       for (PetscInt i=0; i < dof - cdof; i++)
2539         pblocks[offset - localStart + i] = dof - cdof;
2540       dof  = dof < 0 ? -(dof+1) : dof;
2541       bdof = cdof && (dof-cdof) ? 1 : dof;
2542       if (dof) {
2543         if (bs < 0)          {bs = bdof;}
2544         else if (bs != bdof) {bs = 1;}
2545       }
2546     }
2547     /* Must have same blocksize on all procs (some might have no points) */
2548     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2549     bsLocal[1] = bs;
2550     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject) dm), bsLocal, bsMinMax));
2551     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2552     else bs = bsMinMax[0];
2553     bs = PetscMax(1,bs);
2554     PetscCall(MatSetLocalToGlobalMapping(*J,ltog,ltog));
2555     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2556       PetscCall(MatSetBlockSize(*J, bs));
2557       PetscCall(MatSetUp(*J));
2558     } else {
2559       PetscCall(PetscCalloc4(localSize/bs, &dnz, localSize/bs, &onz, localSize/bs, &dnzu, localSize/bs, &onzu));
2560       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2561       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2562     }
2563     { // Consolidate blocks
2564       PetscInt nblocks = 0;
2565       for (PetscInt i=0; i<localSize; i += PetscMax(1, pblocks[i])) {
2566         if (pblocks[i] == 0) continue;
2567         pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2568         for (PetscInt j=1; j<pblocks[i]; j++) {
2569            PetscCheck(pblocks[i+j] == pblocks[i], PETSC_COMM_SELF, PETSC_ERR_PLIB, "Block of size %" PetscInt_FMT " mismatches entry %" PetscInt_FMT, pblocks[i], pblocks[i+j]);
2570         }
2571       }
2572       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2573     }
2574     PetscCall(PetscFree(pblocks));
2575   }
2576   PetscCall(MatSetDM(*J, dm));
2577   PetscFunctionReturn(0);
2578 }
2579 
2580 /*@
2581   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2582 
2583   Not collective
2584 
2585   Input Parameter:
2586 . mesh - The DMPlex
2587 
2588   Output Parameters:
2589 . subsection - The subdomain section
2590 
2591   Level: developer
2592 
2593 .seealso:
2594 @*/
2595 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2596 {
2597   DM_Plex       *mesh = (DM_Plex*) dm->data;
2598 
2599   PetscFunctionBegin;
2600   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2601   if (!mesh->subdomainSection) {
2602     PetscSection section;
2603     PetscSF      sf;
2604 
2605     PetscCall(PetscSFCreate(PETSC_COMM_SELF,&sf));
2606     PetscCall(DMGetLocalSection(dm,&section));
2607     PetscCall(PetscSectionCreateGlobalSection(section,sf,PETSC_FALSE,PETSC_TRUE,&mesh->subdomainSection));
2608     PetscCall(PetscSFDestroy(&sf));
2609   }
2610   *subsection = mesh->subdomainSection;
2611   PetscFunctionReturn(0);
2612 }
2613 
2614 /*@
2615   DMPlexGetChart - Return the interval for all mesh points [pStart, pEnd)
2616 
2617   Not collective
2618 
2619   Input Parameter:
2620 . mesh - The DMPlex
2621 
2622   Output Parameters:
2623 + pStart - The first mesh point
2624 - pEnd   - The upper bound for mesh points
2625 
2626   Level: beginner
2627 
2628 .seealso: `DMPlexCreate()`, `DMPlexSetChart()`
2629 @*/
2630 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2631 {
2632   DM_Plex       *mesh = (DM_Plex*) dm->data;
2633 
2634   PetscFunctionBegin;
2635   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2636   PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2637   PetscFunctionReturn(0);
2638 }
2639 
2640 /*@
2641   DMPlexSetChart - Set the interval for all mesh points [pStart, pEnd)
2642 
2643   Not collective
2644 
2645   Input Parameters:
2646 + mesh - The DMPlex
2647 . pStart - The first mesh point
2648 - pEnd   - The upper bound for mesh points
2649 
2650   Output Parameters:
2651 
2652   Level: beginner
2653 
2654 .seealso: `DMPlexCreate()`, `DMPlexGetChart()`
2655 @*/
2656 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2657 {
2658   DM_Plex       *mesh = (DM_Plex*) dm->data;
2659 
2660   PetscFunctionBegin;
2661   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2662   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2663   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2664   PetscFunctionReturn(0);
2665 }
2666 
2667 /*@
2668   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2669 
2670   Not collective
2671 
2672   Input Parameters:
2673 + mesh - The DMPlex
2674 - p - The point, which must lie in the chart set with DMPlexSetChart()
2675 
2676   Output Parameter:
2677 . size - The cone size for point p
2678 
2679   Level: beginner
2680 
2681 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2682 @*/
2683 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2684 {
2685   DM_Plex       *mesh = (DM_Plex*) dm->data;
2686 
2687   PetscFunctionBegin;
2688   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2689   PetscValidIntPointer(size, 3);
2690   PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2691   PetscFunctionReturn(0);
2692 }
2693 
2694 /*@
2695   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2696 
2697   Not collective
2698 
2699   Input Parameters:
2700 + mesh - The DMPlex
2701 . p - The point, which must lie in the chart set with DMPlexSetChart()
2702 - size - The cone size for point p
2703 
2704   Output Parameter:
2705 
2706   Note:
2707   This should be called after DMPlexSetChart().
2708 
2709   Level: beginner
2710 
2711 .seealso: `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2712 @*/
2713 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
2714 {
2715   DM_Plex       *mesh = (DM_Plex*) dm->data;
2716 
2717   PetscFunctionBegin;
2718   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2719   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
2720   PetscFunctionReturn(0);
2721 }
2722 
2723 /*@
2724   DMPlexAddConeSize - Add the given number of in-edges to this point in the DAG
2725 
2726   Not collective
2727 
2728   Input Parameters:
2729 + mesh - The DMPlex
2730 . p - The point, which must lie in the chart set with DMPlexSetChart()
2731 - size - The additional cone size for point p
2732 
2733   Output Parameter:
2734 
2735   Note:
2736   This should be called after DMPlexSetChart().
2737 
2738   Level: beginner
2739 
2740 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2741 @*/
2742 PetscErrorCode DMPlexAddConeSize(DM dm, PetscInt p, PetscInt size)
2743 {
2744   DM_Plex       *mesh = (DM_Plex*) dm->data;
2745   PetscFunctionBegin;
2746   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2747   PetscCall(PetscSectionAddDof(mesh->coneSection, p, size));
2748   PetscFunctionReturn(0);
2749 }
2750 
2751 /*@C
2752   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
2753 
2754   Not collective
2755 
2756   Input Parameters:
2757 + dm - The DMPlex
2758 - p - The point, which must lie in the chart set with DMPlexSetChart()
2759 
2760   Output Parameter:
2761 . cone - An array of points which are on the in-edges for point p
2762 
2763   Level: beginner
2764 
2765   Fortran Notes:
2766   Since it returns an array, this routine is only available in Fortran 90, and you must
2767   include petsc.h90 in your code.
2768   You must also call DMPlexRestoreCone() after you finish using the returned array.
2769   DMPlexRestoreCone() is not needed/available in C.
2770 
2771 .seealso: `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`
2772 @*/
2773 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
2774 {
2775   DM_Plex       *mesh = (DM_Plex*) dm->data;
2776   PetscInt       off;
2777 
2778   PetscFunctionBegin;
2779   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2780   PetscValidPointer(cone, 3);
2781   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
2782   *cone = &mesh->cones[off];
2783   PetscFunctionReturn(0);
2784 }
2785 
2786 /*@C
2787   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
2788 
2789   Not collective
2790 
2791   Input Parameters:
2792 + dm - The DMPlex
2793 - p - The IS of points, which must lie in the chart set with DMPlexSetChart()
2794 
2795   Output Parameters:
2796 + pConesSection - PetscSection describing the layout of pCones
2797 - pCones - An array of points which are on the in-edges for the point set p
2798 
2799   Level: intermediate
2800 
2801 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`
2802 @*/
2803 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
2804 {
2805   PetscSection        cs, newcs;
2806   PetscInt            *cones;
2807   PetscInt            *newarr=NULL;
2808   PetscInt            n;
2809 
2810   PetscFunctionBegin;
2811   PetscCall(DMPlexGetCones(dm, &cones));
2812   PetscCall(DMPlexGetConeSection(dm, &cs));
2813   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void**)&newarr) : NULL));
2814   if (pConesSection) *pConesSection = newcs;
2815   if (pCones) {
2816     PetscCall(PetscSectionGetStorageSize(newcs, &n));
2817     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
2818   }
2819   PetscFunctionReturn(0);
2820 }
2821 
2822 /*@
2823   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
2824 
2825   Not collective
2826 
2827   Input Parameters:
2828 + dm - The DMPlex
2829 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2830 
2831   Output Parameter:
2832 . expandedPoints - An array of vertices recursively expanded from input points
2833 
2834   Level: advanced
2835 
2836   Notes:
2837   Like DMPlexGetConeRecursive but returns only the 0-depth IS (i.e. vertices only) and no sections.
2838   There is no corresponding Restore function, just call ISDestroy() on the returned IS to deallocate.
2839 
2840 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetDepth()`
2841 @*/
2842 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
2843 {
2844   IS                  *expandedPointsAll;
2845   PetscInt            depth;
2846 
2847   PetscFunctionBegin;
2848   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2849   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2850   PetscValidPointer(expandedPoints, 3);
2851   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2852   *expandedPoints = expandedPointsAll[0];
2853   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
2854   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2855   PetscFunctionReturn(0);
2856 }
2857 
2858 /*@
2859   DMPlexGetConeRecursive - Expand each given point into its cone points and do that recursively until we end up just with vertices (DAG points of depth 0, i.e. without cones).
2860 
2861   Not collective
2862 
2863   Input Parameters:
2864 + dm - The DMPlex
2865 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2866 
2867   Output Parameters:
2868 + depth - (optional) Size of the output arrays, equal to DMPlex depth, returned by DMPlexGetDepth()
2869 . expandedPoints - (optional) An array of index sets with recursively expanded cones
2870 - sections - (optional) An array of sections which describe mappings from points to their cone points
2871 
2872   Level: advanced
2873 
2874   Notes:
2875   Like DMPlexGetConeTuple() but recursive.
2876 
2877   Array expandedPoints has size equal to depth. Each expandedPoints[d] contains DAG points with maximum depth d, recursively cone-wise expanded from the input points.
2878   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
2879 
2880   Array section has size equal to depth.  Each PetscSection sections[d] realizes mapping from expandedPoints[d+1] (section points) to expandedPoints[d] (section dofs) as follows:
2881   (1) DAG points in expandedPoints[d+1] with depth d+1 to their cone points in expandedPoints[d];
2882   (2) DAG points in expandedPoints[d+1] with depth in [0,d] to the same points in expandedPoints[d].
2883 
2884 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`, `DMPlexGetDepth()`
2885 @*/
2886 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2887 {
2888   const PetscInt      *arr0=NULL, *cone=NULL;
2889   PetscInt            *arr=NULL, *newarr=NULL;
2890   PetscInt            d, depth_, i, n, newn, cn, co, start, end;
2891   IS                  *expandedPoints_;
2892   PetscSection        *sections_;
2893 
2894   PetscFunctionBegin;
2895   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2896   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2897   if (depth) PetscValidIntPointer(depth, 3);
2898   if (expandedPoints) PetscValidPointer(expandedPoints, 4);
2899   if (sections) PetscValidPointer(sections, 5);
2900   PetscCall(ISGetLocalSize(points, &n));
2901   PetscCall(ISGetIndices(points, &arr0));
2902   PetscCall(DMPlexGetDepth(dm, &depth_));
2903   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
2904   PetscCall(PetscCalloc1(depth_, &sections_));
2905   arr = (PetscInt*) arr0; /* this is ok because first generation of arr is not modified */
2906   for (d=depth_-1; d>=0; d--) {
2907     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
2908     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
2909     for (i=0; i<n; i++) {
2910       PetscCall(DMPlexGetDepthStratum(dm, d+1, &start, &end));
2911       if (arr[i] >= start && arr[i] < end) {
2912         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
2913         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
2914       } else {
2915         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
2916       }
2917     }
2918     PetscCall(PetscSectionSetUp(sections_[d]));
2919     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
2920     PetscCall(PetscMalloc1(newn, &newarr));
2921     for (i=0; i<n; i++) {
2922       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
2923       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
2924       if (cn > 1) {
2925         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
2926         PetscCall(PetscMemcpy(&newarr[co], cone, cn*sizeof(PetscInt)));
2927       } else {
2928         newarr[co] = arr[i];
2929       }
2930     }
2931     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
2932     arr = newarr;
2933     n = newn;
2934   }
2935   PetscCall(ISRestoreIndices(points, &arr0));
2936   *depth = depth_;
2937   if (expandedPoints) *expandedPoints = expandedPoints_;
2938   else {
2939     for (d=0; d<depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
2940     PetscCall(PetscFree(expandedPoints_));
2941   }
2942   if (sections) *sections = sections_;
2943   else {
2944     for (d=0; d<depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
2945     PetscCall(PetscFree(sections_));
2946   }
2947   PetscFunctionReturn(0);
2948 }
2949 
2950 /*@
2951   DMPlexRestoreConeRecursive - Deallocates arrays created by DMPlexGetConeRecursive
2952 
2953   Not collective
2954 
2955   Input Parameters:
2956 + dm - The DMPlex
2957 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2958 
2959   Output Parameters:
2960 + depth - (optional) Size of the output arrays, equal to DMPlex depth, returned by DMPlexGetDepth()
2961 . expandedPoints - (optional) An array of recursively expanded cones
2962 - sections - (optional) An array of sections which describe mappings from points to their cone points
2963 
2964   Level: advanced
2965 
2966   Notes:
2967   See DMPlexGetConeRecursive() for details.
2968 
2969 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`, `DMPlexGetDepth()`
2970 @*/
2971 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2972 {
2973   PetscInt            d, depth_;
2974 
2975   PetscFunctionBegin;
2976   PetscCall(DMPlexGetDepth(dm, &depth_));
2977   PetscCheck(!depth || *depth == depth_,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
2978   if (depth) *depth = 0;
2979   if (expandedPoints) {
2980     for (d=0; d<depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
2981     PetscCall(PetscFree(*expandedPoints));
2982   }
2983   if (sections)  {
2984     for (d=0; d<depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
2985     PetscCall(PetscFree(*sections));
2986   }
2987   PetscFunctionReturn(0);
2988 }
2989 
2990 /*@
2991   DMPlexSetCone - Set the points on the in-edges for this point in the DAG; that is these are the points that cover the specific point
2992 
2993   Not collective
2994 
2995   Input Parameters:
2996 + mesh - The DMPlex
2997 . p - The point, which must lie in the chart set with DMPlexSetChart()
2998 - cone - An array of points which are on the in-edges for point p
2999 
3000   Output Parameter:
3001 
3002   Note:
3003   This should be called after all calls to DMPlexSetConeSize() and DMSetUp().
3004 
3005   Level: beginner
3006 
3007 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3008 @*/
3009 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3010 {
3011   DM_Plex       *mesh = (DM_Plex*) dm->data;
3012   PetscInt       pStart, pEnd;
3013   PetscInt       dof, off, c;
3014 
3015   PetscFunctionBegin;
3016   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3017   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3018   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3019   if (dof) PetscValidIntPointer(cone, 3);
3020   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3021   PetscCheck(!(p < pStart) && !(p >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3022   for (c = 0; c < dof; ++c) {
3023     PetscCheck(!(cone[c] < pStart) && !(cone[c] >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", cone[c], pStart, pEnd);
3024     mesh->cones[off+c] = cone[c];
3025   }
3026   PetscFunctionReturn(0);
3027 }
3028 
3029 /*@C
3030   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3031 
3032   Not collective
3033 
3034   Input Parameters:
3035 + mesh - The DMPlex
3036 - p - The point, which must lie in the chart set with DMPlexSetChart()
3037 
3038   Output Parameter:
3039 . coneOrientation - An array of orientations which are on the in-edges for point p. An orientation is an
3040                     integer giving the prescription for cone traversal.
3041 
3042   Level: beginner
3043 
3044   Notes:
3045   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3046   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3047   of o, however it is not necessarily the inverse. To get the inverse, use DMPolytopeTypeComposeOrientationInv()
3048   with the identity.
3049 
3050   Fortran Notes:
3051   Since it returns an array, this routine is only available in Fortran 90, and you must
3052   include petsc.h90 in your code.
3053   You must also call DMPlexRestoreConeOrientation() after you finish using the returned array.
3054   DMPlexRestoreConeOrientation() is not needed/available in C.
3055 
3056 .seealso: `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3057 @*/
3058 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3059 {
3060   DM_Plex       *mesh = (DM_Plex*) dm->data;
3061   PetscInt       off;
3062 
3063   PetscFunctionBegin;
3064   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3065   if (PetscDefined(USE_DEBUG)) {
3066     PetscInt dof;
3067     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3068     if (dof) PetscValidPointer(coneOrientation, 3);
3069   }
3070   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3071 
3072   *coneOrientation = &mesh->coneOrientations[off];
3073   PetscFunctionReturn(0);
3074 }
3075 
3076 /*@
3077   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3078 
3079   Not collective
3080 
3081   Input Parameters:
3082 + mesh - The DMPlex
3083 . p - The point, which must lie in the chart set with DMPlexSetChart()
3084 - coneOrientation - An array of orientations
3085   Output Parameter:
3086 
3087   Notes:
3088   This should be called after all calls to DMPlexSetConeSize() and DMSetUp().
3089 
3090   The meaning of coneOrientation is detailed in DMPlexGetConeOrientation().
3091 
3092   Level: beginner
3093 
3094 .seealso: `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3095 @*/
3096 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3097 {
3098   DM_Plex       *mesh = (DM_Plex*) dm->data;
3099   PetscInt       pStart, pEnd;
3100   PetscInt       dof, off, c;
3101 
3102   PetscFunctionBegin;
3103   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3104   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3105   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3106   if (dof) PetscValidIntPointer(coneOrientation, 3);
3107   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3108   PetscCheck(!(p < pStart) && !(p >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3109   for (c = 0; c < dof; ++c) {
3110     PetscInt cdof, o = coneOrientation[c];
3111 
3112     PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off+c], &cdof));
3113     PetscCheck(!o || (o >= -(cdof+1) && o < cdof),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone orientation %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ". %" PetscInt_FMT ")", o, -(cdof+1), cdof);
3114     mesh->coneOrientations[off+c] = o;
3115   }
3116   PetscFunctionReturn(0);
3117 }
3118 
3119 /*@
3120   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3121 
3122   Not collective
3123 
3124   Input Parameters:
3125 + mesh - The DMPlex
3126 . p - The point, which must lie in the chart set with DMPlexSetChart()
3127 . conePos - The local index in the cone where the point should be put
3128 - conePoint - The mesh point to insert
3129 
3130   Level: beginner
3131 
3132 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3133 @*/
3134 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3135 {
3136   DM_Plex       *mesh = (DM_Plex*) dm->data;
3137   PetscInt       pStart, pEnd;
3138   PetscInt       dof, off;
3139 
3140   PetscFunctionBegin;
3141   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3142   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3143   PetscCheck(!(p < pStart) && !(p >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3144   PetscCheck(!(conePoint < pStart) && !(conePoint >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", conePoint, pStart, pEnd);
3145   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3146   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3147   PetscCheck(!(conePos < 0) && !(conePos >= dof),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone position %" PetscInt_FMT " of point %" PetscInt_FMT " is not in the valid range [0, %" PetscInt_FMT ")", conePos, p, dof);
3148   mesh->cones[off+conePos] = conePoint;
3149   PetscFunctionReturn(0);
3150 }
3151 
3152 /*@
3153   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3154 
3155   Not collective
3156 
3157   Input Parameters:
3158 + mesh - The DMPlex
3159 . p - The point, which must lie in the chart set with DMPlexSetChart()
3160 . conePos - The local index in the cone where the point should be put
3161 - coneOrientation - The point orientation to insert
3162 
3163   Level: beginner
3164 
3165   Notes:
3166   The meaning of coneOrientation values is detailed in DMPlexGetConeOrientation().
3167 
3168 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3169 @*/
3170 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3171 {
3172   DM_Plex       *mesh = (DM_Plex*) dm->data;
3173   PetscInt       pStart, pEnd;
3174   PetscInt       dof, off;
3175 
3176   PetscFunctionBegin;
3177   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3178   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3179   PetscCheck(!(p < pStart) && !(p >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3180   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3181   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3182   PetscCheck(!(conePos < 0) && !(conePos >= dof),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone position %" PetscInt_FMT " of point %" PetscInt_FMT " is not in the valid range [0, %" PetscInt_FMT ")", conePos, p, dof);
3183   mesh->coneOrientations[off+conePos] = coneOrientation;
3184   PetscFunctionReturn(0);
3185 }
3186 
3187 /*@
3188   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3189 
3190   Not collective
3191 
3192   Input Parameters:
3193 + mesh - The DMPlex
3194 - p - The point, which must lie in the chart set with DMPlexSetChart()
3195 
3196   Output Parameter:
3197 . size - The support size for point p
3198 
3199   Level: beginner
3200 
3201 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3202 @*/
3203 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3204 {
3205   DM_Plex       *mesh = (DM_Plex*) dm->data;
3206 
3207   PetscFunctionBegin;
3208   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3209   PetscValidIntPointer(size, 3);
3210   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3211   PetscFunctionReturn(0);
3212 }
3213 
3214 /*@
3215   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3216 
3217   Not collective
3218 
3219   Input Parameters:
3220 + mesh - The DMPlex
3221 . p - The point, which must lie in the chart set with DMPlexSetChart()
3222 - size - The support size for point p
3223 
3224   Output Parameter:
3225 
3226   Note:
3227   This should be called after DMPlexSetChart().
3228 
3229   Level: beginner
3230 
3231 .seealso: `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3232 @*/
3233 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3234 {
3235   DM_Plex       *mesh = (DM_Plex*) dm->data;
3236 
3237   PetscFunctionBegin;
3238   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3239   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3240   PetscFunctionReturn(0);
3241 }
3242 
3243 /*@C
3244   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3245 
3246   Not collective
3247 
3248   Input Parameters:
3249 + mesh - The DMPlex
3250 - p - The point, which must lie in the chart set with DMPlexSetChart()
3251 
3252   Output Parameter:
3253 . support - An array of points which are on the out-edges for point p
3254 
3255   Level: beginner
3256 
3257   Fortran Notes:
3258   Since it returns an array, this routine is only available in Fortran 90, and you must
3259   include petsc.h90 in your code.
3260   You must also call DMPlexRestoreSupport() after you finish using the returned array.
3261   DMPlexRestoreSupport() is not needed/available in C.
3262 
3263 .seealso: `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3264 @*/
3265 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3266 {
3267   DM_Plex       *mesh = (DM_Plex*) dm->data;
3268   PetscInt       off;
3269 
3270   PetscFunctionBegin;
3271   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3272   PetscValidPointer(support, 3);
3273   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3274   *support = &mesh->supports[off];
3275   PetscFunctionReturn(0);
3276 }
3277 
3278 /*@
3279   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3280 
3281   Not collective
3282 
3283   Input Parameters:
3284 + mesh - The DMPlex
3285 . p - The point, which must lie in the chart set with DMPlexSetChart()
3286 - support - An array of points which are on the out-edges for point p
3287 
3288   Output Parameter:
3289 
3290   Note:
3291   This should be called after all calls to DMPlexSetSupportSize() and DMSetUp().
3292 
3293   Level: beginner
3294 
3295 .seealso: `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3296 @*/
3297 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3298 {
3299   DM_Plex       *mesh = (DM_Plex*) dm->data;
3300   PetscInt       pStart, pEnd;
3301   PetscInt       dof, off, c;
3302 
3303   PetscFunctionBegin;
3304   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3305   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3306   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3307   if (dof) PetscValidIntPointer(support, 3);
3308   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3309   PetscCheck(!(p < pStart) && !(p >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3310   for (c = 0; c < dof; ++c) {
3311     PetscCheck(!(support[c] < pStart) && !(support[c] >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Support point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", support[c], pStart, pEnd);
3312     mesh->supports[off+c] = support[c];
3313   }
3314   PetscFunctionReturn(0);
3315 }
3316 
3317 /*@
3318   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3319 
3320   Not collective
3321 
3322   Input Parameters:
3323 + mesh - The DMPlex
3324 . p - The point, which must lie in the chart set with DMPlexSetChart()
3325 . supportPos - The local index in the cone where the point should be put
3326 - supportPoint - The mesh point to insert
3327 
3328   Level: beginner
3329 
3330 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3331 @*/
3332 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3333 {
3334   DM_Plex       *mesh = (DM_Plex*) dm->data;
3335   PetscInt       pStart, pEnd;
3336   PetscInt       dof, off;
3337 
3338   PetscFunctionBegin;
3339   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3340   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3341   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3342   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3343   PetscCheck(!(p < pStart) && !(p >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3344   PetscCheck(!(supportPoint < pStart) && !(supportPoint >= pEnd),PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Support point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", supportPoint, pStart, pEnd);
3345   PetscCheck(supportPos < dof,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Support position %" PetscInt_FMT " of point %" PetscInt_FMT " is not in the valid range [0, %" PetscInt_FMT ")", supportPos, p, dof);
3346   mesh->supports[off+supportPos] = supportPoint;
3347   PetscFunctionReturn(0);
3348 }
3349 
3350 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3351 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3352 {
3353   switch (ct) {
3354     case DM_POLYTOPE_SEGMENT:
3355       if (o == -1) return -2;
3356       break;
3357     case DM_POLYTOPE_TRIANGLE:
3358       if (o == -3) return -1;
3359       if (o == -2) return -3;
3360       if (o == -1) return -2;
3361       break;
3362     case DM_POLYTOPE_QUADRILATERAL:
3363       if (o == -4) return -2;
3364       if (o == -3) return -1;
3365       if (o == -2) return -4;
3366       if (o == -1) return -3;
3367       break;
3368     default: return o;
3369   }
3370   return o;
3371 }
3372 
3373 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3374 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3375 {
3376   switch (ct) {
3377     case DM_POLYTOPE_SEGMENT:
3378       if ((o == -2) || (o == 1)) return -1;
3379       if (o == -1) return 0;
3380       break;
3381     case DM_POLYTOPE_TRIANGLE:
3382       if (o == -3) return -2;
3383       if (o == -2) return -1;
3384       if (o == -1) return -3;
3385       break;
3386     case DM_POLYTOPE_QUADRILATERAL:
3387       if (o == -4) return -2;
3388       if (o == -3) return -1;
3389       if (o == -2) return -4;
3390       if (o == -1) return -3;
3391       break;
3392     default: return o;
3393   }
3394   return o;
3395 }
3396 
3397 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3398 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3399 {
3400   PetscInt       pStart, pEnd, p;
3401 
3402   PetscFunctionBegin;
3403   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3404   for (p = pStart; p < pEnd; ++p) {
3405     const PetscInt *cone, *ornt;
3406     PetscInt        coneSize, c;
3407 
3408     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3409     PetscCall(DMPlexGetCone(dm, p, &cone));
3410     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3411     for (c = 0; c < coneSize; ++c) {
3412       DMPolytopeType ct;
3413       const PetscInt o = ornt[c];
3414 
3415       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3416       switch (ct) {
3417         case DM_POLYTOPE_SEGMENT:
3418           if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3419           if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3420           break;
3421         case DM_POLYTOPE_TRIANGLE:
3422           if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3423           if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3424           if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3425           break;
3426         case DM_POLYTOPE_QUADRILATERAL:
3427           if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3428           if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3429           if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3430           if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3431           break;
3432         default: break;
3433       }
3434     }
3435   }
3436   PetscFunctionReturn(0);
3437 }
3438 
3439 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3440 {
3441   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3442   PetscInt       *closure;
3443   const PetscInt *tmp = NULL, *tmpO = NULL;
3444   PetscInt        off = 0, tmpSize, t;
3445 
3446   PetscFunctionBeginHot;
3447   if (ornt) {
3448     PetscCall(DMPlexGetCellType(dm, p, &ct));
3449     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3450   }
3451   if (*points) {
3452     closure = *points;
3453   } else {
3454     PetscInt maxConeSize, maxSupportSize;
3455     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3456     PetscCall(DMGetWorkArray(dm, 2*(PetscMax(maxConeSize, maxSupportSize)+1), MPIU_INT, &closure));
3457   }
3458   if (useCone) {
3459     PetscCall(DMPlexGetConeSize(dm, p, &tmpSize));
3460     PetscCall(DMPlexGetCone(dm, p, &tmp));
3461     PetscCall(DMPlexGetConeOrientation(dm, p, &tmpO));
3462   } else {
3463     PetscCall(DMPlexGetSupportSize(dm, p, &tmpSize));
3464     PetscCall(DMPlexGetSupport(dm, p, &tmp));
3465   }
3466   if (ct == DM_POLYTOPE_UNKNOWN) {
3467     closure[off++] = p;
3468     closure[off++] = 0;
3469     for (t = 0; t < tmpSize; ++t) {
3470       closure[off++] = tmp[t];
3471       closure[off++] = tmpO ? tmpO[t] : 0;
3472     }
3473   } else {
3474     const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, ornt);
3475 
3476     /* We assume that cells with a valid type have faces with a valid type */
3477     closure[off++] = p;
3478     closure[off++] = ornt;
3479     for (t = 0; t < tmpSize; ++t) {
3480       DMPolytopeType ft;
3481 
3482       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3483       closure[off++] = tmp[arr[t]];
3484       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3485     }
3486   }
3487   if (numPoints) *numPoints = tmpSize+1;
3488   if (points)    *points    = closure;
3489   PetscFunctionReturn(0);
3490 }
3491 
3492 /* We need a special tensor verison becasue we want to allow duplicate points in the endcaps for hybrid cells */
3493 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3494 {
3495   const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, o);
3496   const PetscInt *cone, *ornt;
3497   PetscInt       *pts,  *closure = NULL;
3498   DMPolytopeType  ft;
3499   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3500   PetscInt        dim, coneSize, c, d, clSize, cl;
3501 
3502   PetscFunctionBeginHot;
3503   PetscCall(DMGetDimension(dm, &dim));
3504   PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
3505   PetscCall(DMPlexGetCone(dm, point, &cone));
3506   PetscCall(DMPlexGetConeOrientation(dm, point, &ornt));
3507   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3508   coneSeries    = (maxConeSize    > 1) ? ((PetscPowInt(maxConeSize,    dim+1)-1)/(maxConeSize-1))    : dim+1;
3509   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim+1)-1)/(maxSupportSize-1)) : dim+1;
3510   maxSize       = PetscMax(coneSeries, supportSeries);
3511   if (*points) {pts  = *points;}
3512   else         PetscCall(DMGetWorkArray(dm, 2*maxSize, MPIU_INT, &pts));
3513   c    = 0;
3514   pts[c++] = point;
3515   pts[c++] = o;
3516   PetscCall(DMPlexGetCellType(dm, cone[arr[0*2+0]], &ft));
3517   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0*2+0]], DMPolytopeTypeComposeOrientation(ft, arr[0*2+1], ornt[0]), useCone, &clSize, &closure));
3518   for (cl = 0; cl < clSize*2; cl += 2) {pts[c++] = closure[cl]; pts[c++] = closure[cl+1];}
3519   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1*2+0]], DMPolytopeTypeComposeOrientation(ft, arr[1*2+1], ornt[1]), useCone, &clSize, &closure));
3520   for (cl = 0; cl < clSize*2; cl += 2) {pts[c++] = closure[cl]; pts[c++] = closure[cl+1];}
3521   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3522   for (d = 2; d < coneSize; ++d) {
3523     PetscCall(DMPlexGetCellType(dm, cone[arr[d*2+0]], &ft));
3524     pts[c++] = cone[arr[d*2+0]];
3525     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d*2+1], ornt[d]);
3526   }
3527   if (dim >= 3) {
3528     for (d = 2; d < coneSize; ++d) {
3529       const PetscInt  fpoint = cone[arr[d*2+0]];
3530       const PetscInt *fcone, *fornt;
3531       PetscInt        fconeSize, fc, i;
3532 
3533       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3534       const PetscInt *farr = DMPolytopeTypeGetArrangment(ft, DMPolytopeTypeComposeOrientation(ft, arr[d*2+1], ornt[d]));
3535       PetscCall(DMPlexGetConeSize(dm, fpoint, &fconeSize));
3536       PetscCall(DMPlexGetCone(dm, fpoint, &fcone));
3537       PetscCall(DMPlexGetConeOrientation(dm, fpoint, &fornt));
3538       for (fc = 0; fc < fconeSize; ++fc) {
3539         const PetscInt cp = fcone[farr[fc*2+0]];
3540         const PetscInt co = farr[fc*2+1];
3541 
3542         for (i = 0; i < c; i += 2) if (pts[i] == cp) break;
3543         if (i == c) {
3544           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3545           pts[c++] = cp;
3546           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc*2+0]]);
3547         }
3548       }
3549     }
3550   }
3551   *numPoints = c/2;
3552   *points    = pts;
3553   PetscFunctionReturn(0);
3554 }
3555 
3556 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3557 {
3558   DMPolytopeType ct;
3559   PetscInt      *closure, *fifo;
3560   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3561   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3562   PetscInt       depth, maxSize;
3563 
3564   PetscFunctionBeginHot;
3565   PetscCall(DMPlexGetDepth(dm, &depth));
3566   if (depth == 1) {
3567     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3568     PetscFunctionReturn(0);
3569   }
3570   PetscCall(DMPlexGetCellType(dm, p, &ct));
3571   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3572   if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
3573     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3574     PetscFunctionReturn(0);
3575   }
3576   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3577   coneSeries    = (maxConeSize    > 1) ? ((PetscPowInt(maxConeSize,    depth+1)-1)/(maxConeSize-1))    : depth+1;
3578   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth+1)-1)/(maxSupportSize-1)) : depth+1;
3579   maxSize       = PetscMax(coneSeries, supportSeries);
3580   PetscCall(DMGetWorkArray(dm, 3*maxSize, MPIU_INT, &fifo));
3581   if (*points) {closure = *points;}
3582   else         PetscCall(DMGetWorkArray(dm, 2*maxSize, MPIU_INT, &closure));
3583   closure[closureSize++] = p;
3584   closure[closureSize++] = ornt;
3585   fifo[fifoSize++]       = p;
3586   fifo[fifoSize++]       = ornt;
3587   fifo[fifoSize++]       = ct;
3588   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3589   while (fifoSize - fifoStart) {
3590     const PetscInt       q    = fifo[fifoStart++];
3591     const PetscInt       o    = fifo[fifoStart++];
3592     const DMPolytopeType qt   = (DMPolytopeType) fifo[fifoStart++];
3593     const PetscInt      *qarr = DMPolytopeTypeGetArrangment(qt, o);
3594     const PetscInt      *tmp, *tmpO;
3595     PetscInt             tmpSize, t;
3596 
3597     if (PetscDefined(USE_DEBUG)) {
3598       PetscInt nO = DMPolytopeTypeGetNumArrangments(qt)/2;
3599       PetscCheck(!o || !(o >= nO || o < -nO),PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid orientation %" PetscInt_FMT " not in [%" PetscInt_FMT ",%" PetscInt_FMT ") for %s %" PetscInt_FMT, o, -nO, nO, DMPolytopeTypes[qt], q);
3600     }
3601     if (useCone) {
3602       PetscCall(DMPlexGetConeSize(dm, q, &tmpSize));
3603       PetscCall(DMPlexGetCone(dm, q, &tmp));
3604       PetscCall(DMPlexGetConeOrientation(dm, q, &tmpO));
3605     } else {
3606       PetscCall(DMPlexGetSupportSize(dm, q, &tmpSize));
3607       PetscCall(DMPlexGetSupport(dm, q, &tmp));
3608       tmpO = NULL;
3609     }
3610     for (t = 0; t < tmpSize; ++t) {
3611       const PetscInt ip = useCone && qarr ? qarr[t*2]   : t;
3612       const PetscInt io = useCone && qarr ? qarr[t*2+1] : 0;
3613       const PetscInt cp = tmp[ip];
3614       PetscCall(DMPlexGetCellType(dm, cp, &ct));
3615       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
3616       PetscInt       c;
3617 
3618       /* Check for duplicate */
3619       for (c = 0; c < closureSize; c += 2) {
3620         if (closure[c] == cp) break;
3621       }
3622       if (c == closureSize) {
3623         closure[closureSize++] = cp;
3624         closure[closureSize++] = co;
3625         fifo[fifoSize++]       = cp;
3626         fifo[fifoSize++]       = co;
3627         fifo[fifoSize++]       = ct;
3628       }
3629     }
3630   }
3631   PetscCall(DMRestoreWorkArray(dm, 3*maxSize, MPIU_INT, &fifo));
3632   if (numPoints) *numPoints = closureSize/2;
3633   if (points)    *points    = closure;
3634   PetscFunctionReturn(0);
3635 }
3636 
3637 /*@C
3638   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
3639 
3640   Not collective
3641 
3642   Input Parameters:
3643 + dm      - The DMPlex
3644 . p       - The mesh point
3645 - useCone - PETSC_TRUE for the closure, otherwise return the star
3646 
3647   Input/Output Parameter:
3648 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
3649            if NULL on input, internal storage will be returned, otherwise the provided array is used
3650 
3651   Output Parameter:
3652 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3653 
3654   Note:
3655   If using internal storage (points is NULL on input), each call overwrites the last output.
3656 
3657   Fortran Notes:
3658   Since it returns an array, this routine is only available in Fortran 90, and you must include petsc.h90 in your code.
3659 
3660   The numPoints argument is not present in the Fortran 90 binding since it is internal to the array.
3661 
3662   Level: beginner
3663 
3664 .seealso: `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3665 @*/
3666 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3667 {
3668   PetscFunctionBeginHot;
3669   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3670   if (numPoints) PetscValidIntPointer(numPoints, 4);
3671   if (points)    PetscValidPointer(points, 5);
3672   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
3673   PetscFunctionReturn(0);
3674 }
3675 
3676 /*@C
3677   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
3678 
3679   Not collective
3680 
3681   Input Parameters:
3682 + dm        - The DMPlex
3683 . p         - The mesh point
3684 . useCone   - PETSC_TRUE for the closure, otherwise return the star
3685 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3686 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
3687 
3688   Note:
3689   If not using internal storage (points is not NULL on input), this call is unnecessary
3690 
3691   Fortran Notes:
3692   Since it returns an array, this routine is only available in Fortran 90, and you must include petsc.h90 in your code.
3693 
3694   The numPoints argument is not present in the Fortran 90 binding since it is internal to the array.
3695 
3696   Level: beginner
3697 
3698 .seealso: `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3699 @*/
3700 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3701 {
3702   PetscFunctionBeginHot;
3703   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3704   if (numPoints) *numPoints = 0;
3705   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
3706   PetscFunctionReturn(0);
3707 }
3708 
3709 /*@
3710   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
3711 
3712   Not collective
3713 
3714   Input Parameter:
3715 . mesh - The DMPlex
3716 
3717   Output Parameters:
3718 + maxConeSize - The maximum number of in-edges
3719 - maxSupportSize - The maximum number of out-edges
3720 
3721   Level: beginner
3722 
3723 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3724 @*/
3725 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
3726 {
3727   DM_Plex *mesh = (DM_Plex*) dm->data;
3728 
3729   PetscFunctionBegin;
3730   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3731   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
3732   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
3733   PetscFunctionReturn(0);
3734 }
3735 
3736 PetscErrorCode DMSetUp_Plex(DM dm)
3737 {
3738   DM_Plex       *mesh = (DM_Plex*) dm->data;
3739   PetscInt       size, maxSupportSize;
3740 
3741   PetscFunctionBegin;
3742   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3743   PetscCall(PetscSectionSetUp(mesh->coneSection));
3744   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
3745   PetscCall(PetscMalloc1(size, &mesh->cones));
3746   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
3747   PetscCall(PetscLogObjectMemory((PetscObject) dm, size*2*sizeof(PetscInt)));
3748   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
3749   if (maxSupportSize) {
3750     PetscCall(PetscSectionSetUp(mesh->supportSection));
3751     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
3752     PetscCall(PetscMalloc1(size, &mesh->supports));
3753     PetscCall(PetscLogObjectMemory((PetscObject) dm, size*sizeof(PetscInt)));
3754   }
3755   PetscFunctionReturn(0);
3756 }
3757 
3758 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
3759 {
3760   PetscFunctionBegin;
3761   if (subdm) PetscCall(DMClone(dm, subdm));
3762   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, is, subdm));
3763   if (subdm) {(*subdm)->useNatural = dm->useNatural;}
3764   if (dm->useNatural && dm->sfMigration) {
3765     PetscSF        sfMigrationInv,sfNatural;
3766     PetscSection   section, sectionSeq;
3767 
3768     (*subdm)->sfMigration = dm->sfMigration;
3769     PetscCall(PetscObjectReference((PetscObject) dm->sfMigration));
3770     PetscCall(DMGetLocalSection((*subdm), &section));
3771     PetscCall(PetscSFCreateInverseSF((*subdm)->sfMigration, &sfMigrationInv));
3772     PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject) (*subdm)), &sectionSeq));
3773     PetscCall(PetscSFDistributeSection(sfMigrationInv, section, NULL, sectionSeq));
3774 
3775     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, sectionSeq, (*subdm)->sfMigration, &sfNatural));
3776     (*subdm)->sfNatural = sfNatural;
3777     PetscCall(PetscSectionDestroy(&sectionSeq));
3778     PetscCall(PetscSFDestroy(&sfMigrationInv));
3779   }
3780   PetscFunctionReturn(0);
3781 }
3782 
3783 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
3784 {
3785   PetscInt       i = 0;
3786 
3787   PetscFunctionBegin;
3788   PetscCall(DMClone(dms[0], superdm));
3789   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
3790   (*superdm)->useNatural = PETSC_FALSE;
3791   for (i = 0; i < len; i++) {
3792     if (dms[i]->useNatural && dms[i]->sfMigration) {
3793       PetscSF        sfMigrationInv,sfNatural;
3794       PetscSection   section, sectionSeq;
3795 
3796       (*superdm)->sfMigration = dms[i]->sfMigration;
3797       PetscCall(PetscObjectReference((PetscObject) dms[i]->sfMigration));
3798       (*superdm)->useNatural = PETSC_TRUE;
3799       PetscCall(DMGetLocalSection((*superdm), &section));
3800       PetscCall(PetscSFCreateInverseSF((*superdm)->sfMigration, &sfMigrationInv));
3801       PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject) (*superdm)), &sectionSeq));
3802       PetscCall(PetscSFDistributeSection(sfMigrationInv, section, NULL, sectionSeq));
3803 
3804       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, sectionSeq, (*superdm)->sfMigration, &sfNatural));
3805       (*superdm)->sfNatural = sfNatural;
3806       PetscCall(PetscSectionDestroy(&sectionSeq));
3807       PetscCall(PetscSFDestroy(&sfMigrationInv));
3808       break;
3809     }
3810   }
3811   PetscFunctionReturn(0);
3812 }
3813 
3814 /*@
3815   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
3816 
3817   Not collective
3818 
3819   Input Parameter:
3820 . mesh - The DMPlex
3821 
3822   Output Parameter:
3823 
3824   Note:
3825   This should be called after all calls to DMPlexSetCone()
3826 
3827   Level: beginner
3828 
3829 .seealso: `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
3830 @*/
3831 PetscErrorCode DMPlexSymmetrize(DM dm)
3832 {
3833   DM_Plex       *mesh = (DM_Plex*) dm->data;
3834   PetscInt      *offsets;
3835   PetscInt       supportSize;
3836   PetscInt       pStart, pEnd, p;
3837 
3838   PetscFunctionBegin;
3839   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3840   PetscCheck(!mesh->supports,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
3841   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize,dm,0,0,0));
3842   /* Calculate support sizes */
3843   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3844   for (p = pStart; p < pEnd; ++p) {
3845     PetscInt dof, off, c;
3846 
3847     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3848     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3849     for (c = off; c < off+dof; ++c) {
3850       PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
3851     }
3852   }
3853   PetscCall(PetscSectionSetUp(mesh->supportSection));
3854   /* Calculate supports */
3855   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
3856   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
3857   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
3858   for (p = pStart; p < pEnd; ++p) {
3859     PetscInt dof, off, c;
3860 
3861     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3862     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3863     for (c = off; c < off+dof; ++c) {
3864       const PetscInt q = mesh->cones[c];
3865       PetscInt       offS;
3866 
3867       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
3868 
3869       mesh->supports[offS+offsets[q]] = p;
3870       ++offsets[q];
3871     }
3872   }
3873   PetscCall(PetscFree(offsets));
3874   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize,dm,0,0,0));
3875   PetscFunctionReturn(0);
3876 }
3877 
3878 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
3879 {
3880   IS             stratumIS;
3881 
3882   PetscFunctionBegin;
3883   if (pStart >= pEnd) PetscFunctionReturn(0);
3884   if (PetscDefined(USE_DEBUG)) {
3885     PetscInt  qStart, qEnd, numLevels, level;
3886     PetscBool overlap = PETSC_FALSE;
3887     PetscCall(DMLabelGetNumValues(label, &numLevels));
3888     for (level = 0; level < numLevels; level++) {
3889       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
3890       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {overlap = PETSC_TRUE; break;}
3891     }
3892     PetscCheck(!overlap,PETSC_COMM_SELF, PETSC_ERR_PLIB, "New depth %" PetscInt_FMT " range [%" PetscInt_FMT ",%" PetscInt_FMT ") overlaps with depth %" PetscInt_FMT " range [%" PetscInt_FMT ",%" PetscInt_FMT ")", depth, pStart, pEnd, level, qStart, qEnd);
3893   }
3894   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd-pStart, pStart, 1, &stratumIS));
3895   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
3896   PetscCall(ISDestroy(&stratumIS));
3897   PetscFunctionReturn(0);
3898 }
3899 
3900 /*@
3901   DMPlexStratify - The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
3902   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram). The strata group all points of the
3903   same grade, and this function calculates the strata. This grade can be seen as the height (or depth) of the point in
3904   the DAG.
3905 
3906   Collective on dm
3907 
3908   Input Parameter:
3909 . mesh - The DMPlex
3910 
3911   Output Parameter:
3912 
3913   Notes:
3914   Concretely, DMPlexStratify() creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
3915   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
3916   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through DMPlexGetDepthLabel() or DMPlexGetDepthStratum(), or
3917   manually via DMGetLabel().  The height is defined implicitly by height = maxDimension - depth, and can be accessed
3918   via DMPlexGetHeightStratum().  For example, cells have height 0 and faces have height 1.
3919 
3920   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
3921   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
3922   we had a mesh consisting of one triangle (c0) and three vertices (v0, v1, v2), and only one edge is on the boundary so we choose
3923   to interpolate only that one (e0), so that
3924 $  cone(c0) = {e0, v2}
3925 $  cone(e0) = {v0, v1}
3926   If DMPlexStratify() is run on this mesh, it will give depths
3927 $  depth 0 = {v0, v1, v2}
3928 $  depth 1 = {e0, c0}
3929   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
3930 
3931   DMPlexStratify() should be called after all calls to DMPlexSymmetrize()
3932 
3933   Level: beginner
3934 
3935 .seealso: `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
3936 @*/
3937 PetscErrorCode DMPlexStratify(DM dm)
3938 {
3939   DM_Plex       *mesh = (DM_Plex*) dm->data;
3940   DMLabel        label;
3941   PetscInt       pStart, pEnd, p;
3942   PetscInt       numRoots = 0, numLeaves = 0;
3943 
3944   PetscFunctionBegin;
3945   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3946   PetscCall(PetscLogEventBegin(DMPLEX_Stratify,dm,0,0,0));
3947 
3948   /* Create depth label */
3949   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3950   PetscCall(DMCreateLabel(dm, "depth"));
3951   PetscCall(DMPlexGetDepthLabel(dm, &label));
3952 
3953   {
3954     /* Initialize roots and count leaves */
3955     PetscInt sMin = PETSC_MAX_INT;
3956     PetscInt sMax = PETSC_MIN_INT;
3957     PetscInt coneSize, supportSize;
3958 
3959     for (p = pStart; p < pEnd; ++p) {
3960       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3961       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
3962       if (!coneSize && supportSize) {
3963         sMin = PetscMin(p, sMin);
3964         sMax = PetscMax(p, sMax);
3965         ++numRoots;
3966       } else if (!supportSize && coneSize) {
3967         ++numLeaves;
3968       } else if (!supportSize && !coneSize) {
3969         /* Isolated points */
3970         sMin = PetscMin(p, sMin);
3971         sMax = PetscMax(p, sMax);
3972       }
3973     }
3974     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax+1));
3975   }
3976 
3977   if (numRoots + numLeaves == (pEnd - pStart)) {
3978     PetscInt sMin = PETSC_MAX_INT;
3979     PetscInt sMax = PETSC_MIN_INT;
3980     PetscInt coneSize, supportSize;
3981 
3982     for (p = pStart; p < pEnd; ++p) {
3983       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3984       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
3985       if (!supportSize && coneSize) {
3986         sMin = PetscMin(p, sMin);
3987         sMax = PetscMax(p, sMax);
3988       }
3989     }
3990     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax+1));
3991   } else {
3992     PetscInt level = 0;
3993     PetscInt qStart, qEnd, q;
3994 
3995     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
3996     while (qEnd > qStart) {
3997       PetscInt sMin = PETSC_MAX_INT;
3998       PetscInt sMax = PETSC_MIN_INT;
3999 
4000       for (q = qStart; q < qEnd; ++q) {
4001         const PetscInt *support;
4002         PetscInt        supportSize, s;
4003 
4004         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4005         PetscCall(DMPlexGetSupport(dm, q, &support));
4006         for (s = 0; s < supportSize; ++s) {
4007           sMin = PetscMin(support[s], sMin);
4008           sMax = PetscMax(support[s], sMax);
4009         }
4010       }
4011       PetscCall(DMLabelGetNumValues(label, &level));
4012       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax+1));
4013       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4014     }
4015   }
4016   { /* just in case there is an empty process */
4017     PetscInt numValues, maxValues = 0, v;
4018 
4019     PetscCall(DMLabelGetNumValues(label, &numValues));
4020     PetscCallMPI(MPI_Allreduce(&numValues,&maxValues,1,MPIU_INT,MPI_MAX,PetscObjectComm((PetscObject)dm)));
4021     for (v = numValues; v < maxValues; v++) {
4022       PetscCall(DMLabelAddStratum(label, v));
4023     }
4024   }
4025   PetscCall(PetscObjectStateGet((PetscObject) label, &mesh->depthState));
4026   PetscCall(PetscLogEventEnd(DMPLEX_Stratify,dm,0,0,0));
4027   PetscFunctionReturn(0);
4028 }
4029 
4030 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4031 {
4032   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4033   PetscInt       dim, depth, pheight, coneSize;
4034 
4035   PetscFunctionBeginHot;
4036   PetscCall(DMGetDimension(dm, &dim));
4037   PetscCall(DMPlexGetDepth(dm, &depth));
4038   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4039   pheight = depth - pdepth;
4040   if (depth <= 1) {
4041     switch (pdepth) {
4042       case 0: ct = DM_POLYTOPE_POINT;break;
4043       case 1:
4044         switch (coneSize) {
4045           case 2: ct = DM_POLYTOPE_SEGMENT;break;
4046           case 3: ct = DM_POLYTOPE_TRIANGLE;break;
4047           case 4:
4048           switch (dim) {
4049             case 2: ct = DM_POLYTOPE_QUADRILATERAL;break;
4050             case 3: ct = DM_POLYTOPE_TETRAHEDRON;break;
4051             default: break;
4052           }
4053           break;
4054         case 5: ct = DM_POLYTOPE_PYRAMID;break;
4055         case 6: ct = DM_POLYTOPE_TRI_PRISM_TENSOR;break;
4056         case 8: ct = DM_POLYTOPE_HEXAHEDRON;break;
4057         default: break;
4058       }
4059     }
4060   } else {
4061     if (pdepth == 0) {
4062       ct = DM_POLYTOPE_POINT;
4063     } else if (pheight == 0) {
4064       switch (dim) {
4065         case 1:
4066           switch (coneSize) {
4067             case 2: ct = DM_POLYTOPE_SEGMENT;break;
4068             default: break;
4069           }
4070           break;
4071         case 2:
4072           switch (coneSize) {
4073             case 3: ct = DM_POLYTOPE_TRIANGLE;break;
4074             case 4: ct = DM_POLYTOPE_QUADRILATERAL;break;
4075             default: break;
4076           }
4077           break;
4078         case 3:
4079           switch (coneSize) {
4080             case 4: ct = DM_POLYTOPE_TETRAHEDRON;break;
4081             case 5:
4082             {
4083               const PetscInt *cone;
4084               PetscInt        faceConeSize;
4085 
4086               PetscCall(DMPlexGetCone(dm, p, &cone));
4087               PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4088               switch (faceConeSize) {
4089                 case 3: ct = DM_POLYTOPE_TRI_PRISM_TENSOR;break;
4090                 case 4: ct = DM_POLYTOPE_PYRAMID;break;
4091               }
4092             }
4093             break;
4094             case 6: ct = DM_POLYTOPE_HEXAHEDRON;break;
4095             default: break;
4096           }
4097           break;
4098         default: break;
4099       }
4100     } else if (pheight > 0) {
4101       switch (coneSize) {
4102         case 2: ct = DM_POLYTOPE_SEGMENT;break;
4103         case 3: ct = DM_POLYTOPE_TRIANGLE;break;
4104         case 4: ct = DM_POLYTOPE_QUADRILATERAL;break;
4105         default: break;
4106       }
4107     }
4108   }
4109   *pt = ct;
4110   PetscFunctionReturn(0);
4111 }
4112 
4113 /*@
4114   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4115 
4116   Collective on dm
4117 
4118   Input Parameter:
4119 . mesh - The DMPlex
4120 
4121   DMPlexComputeCellTypes() should be called after all calls to DMPlexSymmetrize() and DMPlexStratify()
4122 
4123   Level: developer
4124 
4125   Note: This function is normally called automatically by Plex when a cell type is requested. It creates an
4126   internal DMLabel named "celltype" which can be directly accessed using DMGetLabel(). A user may disable
4127   automatic creation by creating the label manually, using DMCreateLabel(dm, "celltype").
4128 
4129 .seealso: `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4130 @*/
4131 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4132 {
4133   DM_Plex       *mesh;
4134   DMLabel        ctLabel;
4135   PetscInt       pStart, pEnd, p;
4136 
4137   PetscFunctionBegin;
4138   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4139   mesh = (DM_Plex *) dm->data;
4140   PetscCall(DMCreateLabel(dm, "celltype"));
4141   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4142   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4143   for (p = pStart; p < pEnd; ++p) {
4144     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4145     PetscInt       pdepth;
4146 
4147     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4148     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4149     PetscCheck(ct != DM_POLYTOPE_UNKNOWN,PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " is screwed up", p);
4150     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4151   }
4152   PetscCall(PetscObjectStateGet((PetscObject) ctLabel, &mesh->celltypeState));
4153   PetscCall(PetscObjectViewFromOptions((PetscObject) ctLabel, NULL, "-dm_plex_celltypes_view"));
4154   PetscFunctionReturn(0);
4155 }
4156 
4157 /*@C
4158   DMPlexGetJoin - Get an array for the join of the set of points
4159 
4160   Not Collective
4161 
4162   Input Parameters:
4163 + dm - The DMPlex object
4164 . numPoints - The number of input points for the join
4165 - points - The input points
4166 
4167   Output Parameters:
4168 + numCoveredPoints - The number of points in the join
4169 - coveredPoints - The points in the join
4170 
4171   Level: intermediate
4172 
4173   Note: Currently, this is restricted to a single level join
4174 
4175   Fortran Notes:
4176   Since it returns an array, this routine is only available in Fortran 90, and you must
4177   include petsc.h90 in your code.
4178 
4179   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4180 
4181 .seealso: `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4182 @*/
4183 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4184 {
4185   DM_Plex       *mesh = (DM_Plex*) dm->data;
4186   PetscInt      *join[2];
4187   PetscInt       joinSize, i = 0;
4188   PetscInt       dof, off, p, c, m;
4189   PetscInt       maxSupportSize;
4190 
4191   PetscFunctionBegin;
4192   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4193   PetscValidIntPointer(points, 3);
4194   PetscValidIntPointer(numCoveredPoints, 4);
4195   PetscValidPointer(coveredPoints, 5);
4196   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4197   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4198   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4199   /* Copy in support of first point */
4200   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4201   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4202   for (joinSize = 0; joinSize < dof; ++joinSize) {
4203     join[i][joinSize] = mesh->supports[off+joinSize];
4204   }
4205   /* Check each successive support */
4206   for (p = 1; p < numPoints; ++p) {
4207     PetscInt newJoinSize = 0;
4208 
4209     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4210     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4211     for (c = 0; c < dof; ++c) {
4212       const PetscInt point = mesh->supports[off+c];
4213 
4214       for (m = 0; m < joinSize; ++m) {
4215         if (point == join[i][m]) {
4216           join[1-i][newJoinSize++] = point;
4217           break;
4218         }
4219       }
4220     }
4221     joinSize = newJoinSize;
4222     i        = 1-i;
4223   }
4224   *numCoveredPoints = joinSize;
4225   *coveredPoints    = join[i];
4226   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1-i]));
4227   PetscFunctionReturn(0);
4228 }
4229 
4230 /*@C
4231   DMPlexRestoreJoin - Restore an array for the join of the set of points
4232 
4233   Not Collective
4234 
4235   Input Parameters:
4236 + dm - The DMPlex object
4237 . numPoints - The number of input points for the join
4238 - points - The input points
4239 
4240   Output Parameters:
4241 + numCoveredPoints - The number of points in the join
4242 - coveredPoints - The points in the join
4243 
4244   Fortran Notes:
4245   Since it returns an array, this routine is only available in Fortran 90, and you must
4246   include petsc.h90 in your code.
4247 
4248   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4249 
4250   Level: intermediate
4251 
4252 .seealso: `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4253 @*/
4254 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4255 {
4256   PetscFunctionBegin;
4257   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4258   if (points) PetscValidIntPointer(points,3);
4259   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints,4);
4260   PetscValidPointer(coveredPoints, 5);
4261   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void*) coveredPoints));
4262   if (numCoveredPoints) *numCoveredPoints = 0;
4263   PetscFunctionReturn(0);
4264 }
4265 
4266 /*@C
4267   DMPlexGetFullJoin - Get an array for the join of the set of points
4268 
4269   Not Collective
4270 
4271   Input Parameters:
4272 + dm - The DMPlex object
4273 . numPoints - The number of input points for the join
4274 - points - The input points
4275 
4276   Output Parameters:
4277 + numCoveredPoints - The number of points in the join
4278 - coveredPoints - The points in the join
4279 
4280   Fortran Notes:
4281   Since it returns an array, this routine is only available in Fortran 90, and you must
4282   include petsc.h90 in your code.
4283 
4284   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4285 
4286   Level: intermediate
4287 
4288 .seealso: `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4289 @*/
4290 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4291 {
4292   PetscInt      *offsets, **closures;
4293   PetscInt      *join[2];
4294   PetscInt       depth = 0, maxSize, joinSize = 0, i = 0;
4295   PetscInt       p, d, c, m, ms;
4296 
4297   PetscFunctionBegin;
4298   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4299   PetscValidIntPointer(points, 3);
4300   PetscValidIntPointer(numCoveredPoints, 4);
4301   PetscValidPointer(coveredPoints, 5);
4302 
4303   PetscCall(DMPlexGetDepth(dm, &depth));
4304   PetscCall(PetscCalloc1(numPoints, &closures));
4305   PetscCall(DMGetWorkArray(dm, numPoints*(depth+2), MPIU_INT, &offsets));
4306   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4307   maxSize = (ms > 1) ? ((PetscPowInt(ms,depth+1)-1)/(ms-1)) : depth + 1;
4308   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4309   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4310 
4311   for (p = 0; p < numPoints; ++p) {
4312     PetscInt closureSize;
4313 
4314     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4315 
4316     offsets[p*(depth+2)+0] = 0;
4317     for (d = 0; d < depth+1; ++d) {
4318       PetscInt pStart, pEnd, i;
4319 
4320       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4321       for (i = offsets[p*(depth+2)+d]; i < closureSize; ++i) {
4322         if ((pStart > closures[p][i*2]) || (pEnd <= closures[p][i*2])) {
4323           offsets[p*(depth+2)+d+1] = i;
4324           break;
4325         }
4326       }
4327       if (i == closureSize) offsets[p*(depth+2)+d+1] = i;
4328     }
4329     PetscCheck(offsets[p*(depth+2)+depth+1] == closureSize,PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Total size of closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[p*(depth+2)+depth+1], closureSize);
4330   }
4331   for (d = 0; d < depth+1; ++d) {
4332     PetscInt dof;
4333 
4334     /* Copy in support of first point */
4335     dof = offsets[d+1] - offsets[d];
4336     for (joinSize = 0; joinSize < dof; ++joinSize) {
4337       join[i][joinSize] = closures[0][(offsets[d]+joinSize)*2];
4338     }
4339     /* Check each successive cone */
4340     for (p = 1; p < numPoints && joinSize; ++p) {
4341       PetscInt newJoinSize = 0;
4342 
4343       dof = offsets[p*(depth+2)+d+1] - offsets[p*(depth+2)+d];
4344       for (c = 0; c < dof; ++c) {
4345         const PetscInt point = closures[p][(offsets[p*(depth+2)+d]+c)*2];
4346 
4347         for (m = 0; m < joinSize; ++m) {
4348           if (point == join[i][m]) {
4349             join[1-i][newJoinSize++] = point;
4350             break;
4351           }
4352         }
4353       }
4354       joinSize = newJoinSize;
4355       i        = 1-i;
4356     }
4357     if (joinSize) break;
4358   }
4359   *numCoveredPoints = joinSize;
4360   *coveredPoints    = join[i];
4361   for (p = 0; p < numPoints; ++p) {
4362     PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4363   }
4364   PetscCall(PetscFree(closures));
4365   PetscCall(DMRestoreWorkArray(dm, numPoints*(depth+2), MPIU_INT, &offsets));
4366   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1-i]));
4367   PetscFunctionReturn(0);
4368 }
4369 
4370 /*@C
4371   DMPlexGetMeet - Get an array for the meet of the set of points
4372 
4373   Not Collective
4374 
4375   Input Parameters:
4376 + dm - The DMPlex object
4377 . numPoints - The number of input points for the meet
4378 - points - The input points
4379 
4380   Output Parameters:
4381 + numCoveredPoints - The number of points in the meet
4382 - coveredPoints - The points in the meet
4383 
4384   Level: intermediate
4385 
4386   Note: Currently, this is restricted to a single level meet
4387 
4388   Fortran Notes:
4389   Since it returns an array, this routine is only available in Fortran 90, and you must
4390   include petsc.h90 in your code.
4391 
4392   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4393 
4394 .seealso: `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4395 @*/
4396 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4397 {
4398   DM_Plex       *mesh = (DM_Plex*) dm->data;
4399   PetscInt      *meet[2];
4400   PetscInt       meetSize, i = 0;
4401   PetscInt       dof, off, p, c, m;
4402   PetscInt       maxConeSize;
4403 
4404   PetscFunctionBegin;
4405   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4406   PetscValidIntPointer(points, 3);
4407   PetscValidIntPointer(numCoveringPoints, 4);
4408   PetscValidPointer(coveringPoints, 5);
4409   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4410   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4411   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4412   /* Copy in cone of first point */
4413   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4414   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4415   for (meetSize = 0; meetSize < dof; ++meetSize) {
4416     meet[i][meetSize] = mesh->cones[off+meetSize];
4417   }
4418   /* Check each successive cone */
4419   for (p = 1; p < numPoints; ++p) {
4420     PetscInt newMeetSize = 0;
4421 
4422     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4423     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4424     for (c = 0; c < dof; ++c) {
4425       const PetscInt point = mesh->cones[off+c];
4426 
4427       for (m = 0; m < meetSize; ++m) {
4428         if (point == meet[i][m]) {
4429           meet[1-i][newMeetSize++] = point;
4430           break;
4431         }
4432       }
4433     }
4434     meetSize = newMeetSize;
4435     i        = 1-i;
4436   }
4437   *numCoveringPoints = meetSize;
4438   *coveringPoints    = meet[i];
4439   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1-i]));
4440   PetscFunctionReturn(0);
4441 }
4442 
4443 /*@C
4444   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4445 
4446   Not Collective
4447 
4448   Input Parameters:
4449 + dm - The DMPlex object
4450 . numPoints - The number of input points for the meet
4451 - points - The input points
4452 
4453   Output Parameters:
4454 + numCoveredPoints - The number of points in the meet
4455 - coveredPoints - The points in the meet
4456 
4457   Level: intermediate
4458 
4459   Fortran Notes:
4460   Since it returns an array, this routine is only available in Fortran 90, and you must
4461   include petsc.h90 in your code.
4462 
4463   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4464 
4465 .seealso: `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4466 @*/
4467 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4468 {
4469   PetscFunctionBegin;
4470   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4471   if (points) PetscValidIntPointer(points,3);
4472   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints,4);
4473   PetscValidPointer(coveredPoints,5);
4474   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void*) coveredPoints));
4475   if (numCoveredPoints) *numCoveredPoints = 0;
4476   PetscFunctionReturn(0);
4477 }
4478 
4479 /*@C
4480   DMPlexGetFullMeet - Get an array for the meet of the set of points
4481 
4482   Not Collective
4483 
4484   Input Parameters:
4485 + dm - The DMPlex object
4486 . numPoints - The number of input points for the meet
4487 - points - The input points
4488 
4489   Output Parameters:
4490 + numCoveredPoints - The number of points in the meet
4491 - coveredPoints - The points in the meet
4492 
4493   Level: intermediate
4494 
4495   Fortran Notes:
4496   Since it returns an array, this routine is only available in Fortran 90, and you must
4497   include petsc.h90 in your code.
4498 
4499   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4500 
4501 .seealso: `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4502 @*/
4503 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4504 {
4505   PetscInt      *offsets, **closures;
4506   PetscInt      *meet[2];
4507   PetscInt       height = 0, maxSize, meetSize = 0, i = 0;
4508   PetscInt       p, h, c, m, mc;
4509 
4510   PetscFunctionBegin;
4511   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4512   PetscValidIntPointer(points, 3);
4513   PetscValidIntPointer(numCoveredPoints, 4);
4514   PetscValidPointer(coveredPoints, 5);
4515 
4516   PetscCall(DMPlexGetDepth(dm, &height));
4517   PetscCall(PetscMalloc1(numPoints, &closures));
4518   PetscCall(DMGetWorkArray(dm, numPoints*(height+2), MPIU_INT, &offsets));
4519   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4520   maxSize = (mc > 1) ? ((PetscPowInt(mc,height+1)-1)/(mc-1)) : height + 1;
4521   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4522   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4523 
4524   for (p = 0; p < numPoints; ++p) {
4525     PetscInt closureSize;
4526 
4527     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4528 
4529     offsets[p*(height+2)+0] = 0;
4530     for (h = 0; h < height+1; ++h) {
4531       PetscInt pStart, pEnd, i;
4532 
4533       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4534       for (i = offsets[p*(height+2)+h]; i < closureSize; ++i) {
4535         if ((pStart > closures[p][i*2]) || (pEnd <= closures[p][i*2])) {
4536           offsets[p*(height+2)+h+1] = i;
4537           break;
4538         }
4539       }
4540       if (i == closureSize) offsets[p*(height+2)+h+1] = i;
4541     }
4542     PetscCheck(offsets[p*(height+2)+height+1] == closureSize,PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Total size of closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[p*(height+2)+height+1], closureSize);
4543   }
4544   for (h = 0; h < height+1; ++h) {
4545     PetscInt dof;
4546 
4547     /* Copy in cone of first point */
4548     dof = offsets[h+1] - offsets[h];
4549     for (meetSize = 0; meetSize < dof; ++meetSize) {
4550       meet[i][meetSize] = closures[0][(offsets[h]+meetSize)*2];
4551     }
4552     /* Check each successive cone */
4553     for (p = 1; p < numPoints && meetSize; ++p) {
4554       PetscInt newMeetSize = 0;
4555 
4556       dof = offsets[p*(height+2)+h+1] - offsets[p*(height+2)+h];
4557       for (c = 0; c < dof; ++c) {
4558         const PetscInt point = closures[p][(offsets[p*(height+2)+h]+c)*2];
4559 
4560         for (m = 0; m < meetSize; ++m) {
4561           if (point == meet[i][m]) {
4562             meet[1-i][newMeetSize++] = point;
4563             break;
4564           }
4565         }
4566       }
4567       meetSize = newMeetSize;
4568       i        = 1-i;
4569     }
4570     if (meetSize) break;
4571   }
4572   *numCoveredPoints = meetSize;
4573   *coveredPoints    = meet[i];
4574   for (p = 0; p < numPoints; ++p) {
4575     PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
4576   }
4577   PetscCall(PetscFree(closures));
4578   PetscCall(DMRestoreWorkArray(dm, numPoints*(height+2), MPIU_INT, &offsets));
4579   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1-i]));
4580   PetscFunctionReturn(0);
4581 }
4582 
4583 /*@C
4584   DMPlexEqual - Determine if two DMs have the same topology
4585 
4586   Not Collective
4587 
4588   Input Parameters:
4589 + dmA - A DMPlex object
4590 - dmB - A DMPlex object
4591 
4592   Output Parameters:
4593 . equal - PETSC_TRUE if the topologies are identical
4594 
4595   Level: intermediate
4596 
4597   Notes:
4598   We are not solving graph isomorphism, so we do not permutation.
4599 
4600 .seealso: `DMPlexGetCone()`
4601 @*/
4602 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
4603 {
4604   PetscInt       depth, depthB, pStart, pEnd, pStartB, pEndB, p;
4605 
4606   PetscFunctionBegin;
4607   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
4608   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
4609   PetscValidBoolPointer(equal, 3);
4610 
4611   *equal = PETSC_FALSE;
4612   PetscCall(DMPlexGetDepth(dmA, &depth));
4613   PetscCall(DMPlexGetDepth(dmB, &depthB));
4614   if (depth != depthB) PetscFunctionReturn(0);
4615   PetscCall(DMPlexGetChart(dmA, &pStart,  &pEnd));
4616   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
4617   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(0);
4618   for (p = pStart; p < pEnd; ++p) {
4619     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
4620     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
4621 
4622     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
4623     PetscCall(DMPlexGetCone(dmA, p, &cone));
4624     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
4625     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
4626     PetscCall(DMPlexGetCone(dmB, p, &coneB));
4627     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
4628     if (coneSize != coneSizeB) PetscFunctionReturn(0);
4629     for (c = 0; c < coneSize; ++c) {
4630       if (cone[c] != coneB[c]) PetscFunctionReturn(0);
4631       if (ornt[c] != orntB[c]) PetscFunctionReturn(0);
4632     }
4633     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
4634     PetscCall(DMPlexGetSupport(dmA, p, &support));
4635     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
4636     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
4637     if (supportSize != supportSizeB) PetscFunctionReturn(0);
4638     for (s = 0; s < supportSize; ++s) {
4639       if (support[s] != supportB[s]) PetscFunctionReturn(0);
4640     }
4641   }
4642   *equal = PETSC_TRUE;
4643   PetscFunctionReturn(0);
4644 }
4645 
4646 /*@C
4647   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
4648 
4649   Not Collective
4650 
4651   Input Parameters:
4652 + dm         - The DMPlex
4653 . cellDim    - The cell dimension
4654 - numCorners - The number of vertices on a cell
4655 
4656   Output Parameters:
4657 . numFaceVertices - The number of vertices on a face
4658 
4659   Level: developer
4660 
4661   Notes:
4662   Of course this can only work for a restricted set of symmetric shapes
4663 
4664 .seealso: `DMPlexGetCone()`
4665 @*/
4666 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
4667 {
4668   MPI_Comm       comm;
4669 
4670   PetscFunctionBegin;
4671   PetscCall(PetscObjectGetComm((PetscObject)dm,&comm));
4672   PetscValidIntPointer(numFaceVertices,4);
4673   switch (cellDim) {
4674   case 0:
4675     *numFaceVertices = 0;
4676     break;
4677   case 1:
4678     *numFaceVertices = 1;
4679     break;
4680   case 2:
4681     switch (numCorners) {
4682     case 3: /* triangle */
4683       *numFaceVertices = 2; /* Edge has 2 vertices */
4684       break;
4685     case 4: /* quadrilateral */
4686       *numFaceVertices = 2; /* Edge has 2 vertices */
4687       break;
4688     case 6: /* quadratic triangle, tri and quad cohesive Lagrange cells */
4689       *numFaceVertices = 3; /* Edge has 3 vertices */
4690       break;
4691     case 9: /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
4692       *numFaceVertices = 3; /* Edge has 3 vertices */
4693       break;
4694     default:
4695       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4696     }
4697     break;
4698   case 3:
4699     switch (numCorners) {
4700     case 4: /* tetradehdron */
4701       *numFaceVertices = 3; /* Face has 3 vertices */
4702       break;
4703     case 6: /* tet cohesive cells */
4704       *numFaceVertices = 4; /* Face has 4 vertices */
4705       break;
4706     case 8: /* hexahedron */
4707       *numFaceVertices = 4; /* Face has 4 vertices */
4708       break;
4709     case 9: /* tet cohesive Lagrange cells */
4710       *numFaceVertices = 6; /* Face has 6 vertices */
4711       break;
4712     case 10: /* quadratic tetrahedron */
4713       *numFaceVertices = 6; /* Face has 6 vertices */
4714       break;
4715     case 12: /* hex cohesive Lagrange cells */
4716       *numFaceVertices = 6; /* Face has 6 vertices */
4717       break;
4718     case 18: /* quadratic tet cohesive Lagrange cells */
4719       *numFaceVertices = 6; /* Face has 6 vertices */
4720       break;
4721     case 27: /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
4722       *numFaceVertices = 9; /* Face has 9 vertices */
4723       break;
4724     default:
4725       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4726     }
4727     break;
4728   default:
4729     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
4730   }
4731   PetscFunctionReturn(0);
4732 }
4733 
4734 /*@
4735   DMPlexGetDepthLabel - Get the DMLabel recording the depth of each point
4736 
4737   Not Collective
4738 
4739   Input Parameter:
4740 . dm    - The DMPlex object
4741 
4742   Output Parameter:
4743 . depthLabel - The DMLabel recording point depth
4744 
4745   Level: developer
4746 
4747 .seealso: `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
4748 @*/
4749 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
4750 {
4751   PetscFunctionBegin;
4752   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4753   PetscValidPointer(depthLabel, 2);
4754   *depthLabel = dm->depthLabel;
4755   PetscFunctionReturn(0);
4756 }
4757 
4758 /*@
4759   DMPlexGetDepth - Get the depth of the DAG representing this mesh
4760 
4761   Not Collective
4762 
4763   Input Parameter:
4764 . dm    - The DMPlex object
4765 
4766   Output Parameter:
4767 . depth - The number of strata (breadth first levels) in the DAG
4768 
4769   Level: developer
4770 
4771   Notes:
4772   This returns maximum of point depths over all points, i.e. maximum value of the label returned by DMPlexGetDepthLabel().
4773   The point depth is described more in detail in DMPlexGetDepthStratum().
4774   An empty mesh gives -1.
4775 
4776 .seealso: `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
4777 @*/
4778 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
4779 {
4780   DMLabel        label;
4781   PetscInt       d = 0;
4782 
4783   PetscFunctionBegin;
4784   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4785   PetscValidIntPointer(depth, 2);
4786   PetscCall(DMPlexGetDepthLabel(dm, &label));
4787   if (label) PetscCall(DMLabelGetNumValues(label, &d));
4788   *depth = d-1;
4789   PetscFunctionReturn(0);
4790 }
4791 
4792 /*@
4793   DMPlexGetDepthStratum - Get the bounds [start, end) for all points at a certain depth.
4794 
4795   Not Collective
4796 
4797   Input Parameters:
4798 + dm    - The DMPlex object
4799 - depth - The requested depth
4800 
4801   Output Parameters:
4802 + start - The first point at this depth
4803 - end   - One beyond the last point at this depth
4804 
4805   Notes:
4806   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
4807   often "vertices".  If the mesh is "interpolated" (see DMPlexInterpolate()), then depth stratum 1 contains the next
4808   higher dimension, e.g., "edges".
4809 
4810   Level: developer
4811 
4812 .seealso: `DMPlexGetHeightStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
4813 @*/
4814 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
4815 {
4816   DMLabel        label;
4817   PetscInt       pStart, pEnd;
4818 
4819   PetscFunctionBegin;
4820   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4821   if (start) {PetscValidIntPointer(start, 3); *start = 0;}
4822   if (end)   {PetscValidIntPointer(end,   4); *end   = 0;}
4823   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4824   if (pStart == pEnd) PetscFunctionReturn(0);
4825   if (depth < 0) {
4826     if (start) *start = pStart;
4827     if (end)   *end   = pEnd;
4828     PetscFunctionReturn(0);
4829   }
4830   PetscCall(DMPlexGetDepthLabel(dm, &label));
4831   PetscCheck(label,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
4832   PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
4833   PetscFunctionReturn(0);
4834 }
4835 
4836 /*@
4837   DMPlexGetHeightStratum - Get the bounds [start, end) for all points at a certain height.
4838 
4839   Not Collective
4840 
4841   Input Parameters:
4842 + dm     - The DMPlex object
4843 - height - The requested height
4844 
4845   Output Parameters:
4846 + start - The first point at this height
4847 - end   - One beyond the last point at this height
4848 
4849   Notes:
4850   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
4851   points, often called "cells" or "elements".  If the mesh is "interpolated" (see DMPlexInterpolate()), then height
4852   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
4853 
4854   Level: developer
4855 
4856 .seealso: `DMPlexGetDepthStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
4857 @*/
4858 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
4859 {
4860   DMLabel        label;
4861   PetscInt       depth, pStart, pEnd;
4862 
4863   PetscFunctionBegin;
4864   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4865   if (start) {PetscValidIntPointer(start, 3); *start = 0;}
4866   if (end)   {PetscValidIntPointer(end,   4); *end   = 0;}
4867   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4868   if (pStart == pEnd) PetscFunctionReturn(0);
4869   if (height < 0) {
4870     if (start) *start = pStart;
4871     if (end)   *end   = pEnd;
4872     PetscFunctionReturn(0);
4873   }
4874   PetscCall(DMPlexGetDepthLabel(dm, &label));
4875   PetscCheck(label,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
4876   PetscCall(DMLabelGetNumValues(label, &depth));
4877   PetscCall(DMLabelGetStratumBounds(label, depth-1-height, start, end));
4878   PetscFunctionReturn(0);
4879 }
4880 
4881 /*@
4882   DMPlexGetPointDepth - Get the depth of a given point
4883 
4884   Not Collective
4885 
4886   Input Parameters:
4887 + dm    - The DMPlex object
4888 - point - The point
4889 
4890   Output Parameter:
4891 . depth - The depth of the point
4892 
4893   Level: intermediate
4894 
4895 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
4896 @*/
4897 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
4898 {
4899   PetscFunctionBegin;
4900   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4901   PetscValidIntPointer(depth, 3);
4902   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
4903   PetscFunctionReturn(0);
4904 }
4905 
4906 /*@
4907   DMPlexGetPointHeight - Get the height of a given point
4908 
4909   Not Collective
4910 
4911   Input Parameters:
4912 + dm    - The DMPlex object
4913 - point - The point
4914 
4915   Output Parameter:
4916 . height - The height of the point
4917 
4918   Level: intermediate
4919 
4920 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
4921 @*/
4922 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
4923 {
4924   PetscInt       n, pDepth;
4925 
4926   PetscFunctionBegin;
4927   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4928   PetscValidIntPointer(height, 3);
4929   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
4930   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
4931   *height = n - 1 - pDepth;  /* DAG depth is n-1 */
4932   PetscFunctionReturn(0);
4933 }
4934 
4935 /*@
4936   DMPlexGetCellTypeLabel - Get the DMLabel recording the polytope type of each cell
4937 
4938   Not Collective
4939 
4940   Input Parameter:
4941 . dm - The DMPlex object
4942 
4943   Output Parameter:
4944 . celltypeLabel - The DMLabel recording cell polytope type
4945 
4946   Note: This function will trigger automatica computation of cell types. This can be disabled by calling
4947   DMCreateLabel(dm, "celltype") beforehand.
4948 
4949   Level: developer
4950 
4951 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
4952 @*/
4953 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
4954 {
4955   PetscFunctionBegin;
4956   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4957   PetscValidPointer(celltypeLabel, 2);
4958   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
4959   *celltypeLabel = dm->celltypeLabel;
4960   PetscFunctionReturn(0);
4961 }
4962 
4963 /*@
4964   DMPlexGetCellType - Get the polytope type of a given cell
4965 
4966   Not Collective
4967 
4968   Input Parameters:
4969 + dm   - The DMPlex object
4970 - cell - The cell
4971 
4972   Output Parameter:
4973 . celltype - The polytope type of the cell
4974 
4975   Level: intermediate
4976 
4977 .seealso: `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
4978 @*/
4979 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
4980 {
4981   DMLabel        label;
4982   PetscInt       ct;
4983 
4984   PetscFunctionBegin;
4985   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4986   PetscValidPointer(celltype, 3);
4987   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
4988   PetscCall(DMLabelGetValue(label, cell, &ct));
4989   PetscCheck(ct >= 0,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
4990   *celltype = (DMPolytopeType) ct;
4991   PetscFunctionReturn(0);
4992 }
4993 
4994 /*@
4995   DMPlexSetCellType - Set the polytope type of a given cell
4996 
4997   Not Collective
4998 
4999   Input Parameters:
5000 + dm   - The DMPlex object
5001 . cell - The cell
5002 - celltype - The polytope type of the cell
5003 
5004   Note: By default, cell types will be automatically computed using DMPlexComputeCellTypes() before this function
5005   is executed. This function will override the computed type. However, if automatic classification will not succeed
5006   and a user wants to manually specify all types, the classification must be disabled by calling
5007   DMCreaateLabel(dm, "celltype") before getting or setting any cell types.
5008 
5009   Level: advanced
5010 
5011 .seealso: `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5012 @*/
5013 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5014 {
5015   DMLabel        label;
5016 
5017   PetscFunctionBegin;
5018   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5019   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5020   PetscCall(DMLabelSetValue(label, cell, celltype));
5021   PetscFunctionReturn(0);
5022 }
5023 
5024 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5025 {
5026   PetscSection   section, s;
5027   Mat            m;
5028   PetscInt       maxHeight;
5029 
5030   PetscFunctionBegin;
5031   PetscCall(DMClone(dm, cdm));
5032   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5033   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5034   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5035   PetscCall(DMSetLocalSection(*cdm, section));
5036   PetscCall(PetscSectionDestroy(&section));
5037   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
5038   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
5039   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
5040   PetscCall(PetscSectionDestroy(&s));
5041   PetscCall(MatDestroy(&m));
5042 
5043   PetscCall(DMSetNumFields(*cdm, 1));
5044   PetscCall(DMCreateDS(*cdm));
5045   PetscFunctionReturn(0);
5046 }
5047 
5048 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5049 {
5050   Vec coordsLocal, cellCoordsLocal;
5051   DM  coordsDM,    cellCoordsDM;
5052 
5053   PetscFunctionBegin;
5054   *field = NULL;
5055   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5056   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5057   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5058   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5059   if (coordsLocal && coordsDM) {
5060     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5061     else                                 PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5062   }
5063   PetscFunctionReturn(0);
5064 }
5065 
5066 /*@C
5067   DMPlexGetConeSection - Return a section which describes the layout of cone data
5068 
5069   Not Collective
5070 
5071   Input Parameters:
5072 . dm        - The DMPlex object
5073 
5074   Output Parameter:
5075 . section - The PetscSection object
5076 
5077   Level: developer
5078 
5079 .seealso: `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`
5080 @*/
5081 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5082 {
5083   DM_Plex *mesh = (DM_Plex*) dm->data;
5084 
5085   PetscFunctionBegin;
5086   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5087   if (section) *section = mesh->coneSection;
5088   PetscFunctionReturn(0);
5089 }
5090 
5091 /*@C
5092   DMPlexGetSupportSection - Return a section which describes the layout of support data
5093 
5094   Not Collective
5095 
5096   Input Parameters:
5097 . dm        - The DMPlex object
5098 
5099   Output Parameter:
5100 . section - The PetscSection object
5101 
5102   Level: developer
5103 
5104 .seealso: `DMPlexGetConeSection()`
5105 @*/
5106 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5107 {
5108   DM_Plex *mesh = (DM_Plex*) dm->data;
5109 
5110   PetscFunctionBegin;
5111   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5112   if (section) *section = mesh->supportSection;
5113   PetscFunctionReturn(0);
5114 }
5115 
5116 /*@C
5117   DMPlexGetCones - Return cone data
5118 
5119   Not Collective
5120 
5121   Input Parameters:
5122 . dm        - The DMPlex object
5123 
5124   Output Parameter:
5125 . cones - The cone for each point
5126 
5127   Level: developer
5128 
5129 .seealso: `DMPlexGetConeSection()`
5130 @*/
5131 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5132 {
5133   DM_Plex *mesh = (DM_Plex*) dm->data;
5134 
5135   PetscFunctionBegin;
5136   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5137   if (cones) *cones = mesh->cones;
5138   PetscFunctionReturn(0);
5139 }
5140 
5141 /*@C
5142   DMPlexGetConeOrientations - Return cone orientation data
5143 
5144   Not Collective
5145 
5146   Input Parameters:
5147 . dm        - The DMPlex object
5148 
5149   Output Parameter:
5150 . coneOrientations - The array of cone orientations for all points
5151 
5152   Level: developer
5153 
5154   Notes:
5155   The PetscSection returned by DMPlexGetConeSection() partitions coneOrientations into cone orientations of particular points as returned by DMPlexGetConeOrientation().
5156 
5157   The meaning of coneOrientations values is detailed in DMPlexGetConeOrientation().
5158 
5159 .seealso: `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`
5160 @*/
5161 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5162 {
5163   DM_Plex *mesh = (DM_Plex*) dm->data;
5164 
5165   PetscFunctionBegin;
5166   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5167   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5168   PetscFunctionReturn(0);
5169 }
5170 
5171 /******************************** FEM Support **********************************/
5172 
5173 /*
5174  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5175  representing a line in the section.
5176 */
5177 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(PetscSection section,PetscInt field,PetscInt line,PetscBool vertexchart,PetscInt *Nc,PetscInt *k)
5178 {
5179   PetscFunctionBeginHot;
5180   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5181   if (line < 0) {
5182     *k = 0;
5183     *Nc = 0;
5184   } else if (vertexchart) {            /* If we only have a vertex chart, we must have degree k=1 */
5185     *k = 1;
5186   } else {                      /* Assume the full interpolated mesh is in the chart; lines in particular */
5187     /* An order k SEM disc has k-1 dofs on an edge */
5188     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5189     *k = *k / *Nc + 1;
5190   }
5191   PetscFunctionReturn(0);
5192 }
5193 
5194 /*@
5195 
5196   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5197   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5198   section provided (or the section of the DM).
5199 
5200   Input Parameters:
5201 + dm      - The DM
5202 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or PETSC_DETERMINE
5203 - section - The PetscSection to reorder, or NULL for the default section
5204 
5205   Note: The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5206   degree of the basis.
5207 
5208   Example:
5209   A typical interpolated single-quad mesh might order points as
5210 .vb
5211   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5212 
5213   v4 -- e6 -- v3
5214   |           |
5215   e7    c0    e8
5216   |           |
5217   v1 -- e5 -- v2
5218 .ve
5219 
5220   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5221   dofs in the order of points, e.g.,
5222 .vb
5223     c0 -> [0,1,2,3]
5224     v1 -> [4]
5225     ...
5226     e5 -> [8, 9]
5227 .ve
5228 
5229   which corresponds to the dofs
5230 .vb
5231     6   10  11  7
5232     13  2   3   15
5233     12  0   1   14
5234     4   8   9   5
5235 .ve
5236 
5237   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5238 .vb
5239   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5240 .ve
5241 
5242   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5243 .vb
5244    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5245 .ve
5246 
5247   Level: developer
5248 
5249 .seealso: `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5250 @*/
5251 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5252 {
5253   DMLabel        label;
5254   PetscInt       dim, depth = -1, eStart = -1, Nf;
5255   PetscBool      vertexchart;
5256 
5257   PetscFunctionBegin;
5258   PetscCall(DMGetDimension(dm, &dim));
5259   if (dim < 1) PetscFunctionReturn(0);
5260   if (point < 0) {
5261     PetscInt sStart,sEnd;
5262 
5263     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5264     point = sEnd-sStart ? sStart : point;
5265   }
5266   PetscCall(DMPlexGetDepthLabel(dm, &label));
5267   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5268   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5269   if (depth == 1) {eStart = point;}
5270   else if  (depth == dim) {
5271     const PetscInt *cone;
5272 
5273     PetscCall(DMPlexGetCone(dm, point, &cone));
5274     if (dim == 2) eStart = cone[0];
5275     else if (dim == 3) {
5276       const PetscInt *cone2;
5277       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5278       eStart = cone2[0];
5279     } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Point %" PetscInt_FMT " of depth %" PetscInt_FMT " cannot be used to bootstrap spectral ordering for dim %" PetscInt_FMT, point, depth, dim);
5280   } else PetscCheck(depth < 0,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Point %" PetscInt_FMT " of depth %" PetscInt_FMT " cannot be used to bootstrap spectral ordering for dim %" PetscInt_FMT, point, depth, dim);
5281   {                             /* Determine whether the chart covers all points or just vertices. */
5282     PetscInt pStart,pEnd,cStart,cEnd;
5283     PetscCall(DMPlexGetDepthStratum(dm,0,&pStart,&pEnd));
5284     PetscCall(PetscSectionGetChart(section,&cStart,&cEnd));
5285     if (pStart == cStart && pEnd == cEnd) vertexchart = PETSC_TRUE;      /* Only vertices are in the chart */
5286     else if (cStart <= point && point < cEnd) vertexchart = PETSC_FALSE; /* Some interpolated points exist in the chart */
5287     else vertexchart = PETSC_TRUE;                                       /* Some interpolated points are not in chart; assume dofs only at cells and vertices */
5288   }
5289   PetscCall(PetscSectionGetNumFields(section, &Nf));
5290   for (PetscInt d=1; d<=dim; d++) {
5291     PetscInt k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5292     PetscInt *perm;
5293 
5294     for (f = 0; f < Nf; ++f) {
5295       PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5296       size += PetscPowInt(k+1, d)*Nc;
5297     }
5298     PetscCall(PetscMalloc1(size, &perm));
5299     for (f = 0; f < Nf; ++f) {
5300       switch (d) {
5301       case 1:
5302         PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5303         /*
5304          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5305          We want              [ vtx0; edge of length k-1; vtx1 ]
5306          */
5307         for (c=0; c<Nc; c++,offset++) perm[offset] = (k-1)*Nc + c + foffset;
5308         for (i=0; i<k-1; i++) for (c=0; c<Nc; c++,offset++) perm[offset] = i*Nc + c + foffset;
5309         for (c=0; c<Nc; c++,offset++) perm[offset] = k*Nc + c + foffset;
5310         foffset = offset;
5311         break;
5312       case 2:
5313         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5314         PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5315         /* The SEM order is
5316 
5317          v_lb, {e_b}, v_rb,
5318          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5319          v_lt, reverse {e_t}, v_rt
5320          */
5321         {
5322           const PetscInt of   = 0;
5323           const PetscInt oeb  = of   + PetscSqr(k-1);
5324           const PetscInt oer  = oeb  + (k-1);
5325           const PetscInt oet  = oer  + (k-1);
5326           const PetscInt oel  = oet  + (k-1);
5327           const PetscInt ovlb = oel  + (k-1);
5328           const PetscInt ovrb = ovlb + 1;
5329           const PetscInt ovrt = ovrb + 1;
5330           const PetscInt ovlt = ovrt + 1;
5331           PetscInt       o;
5332 
5333           /* bottom */
5334           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb*Nc + c + foffset;
5335           for (o = oeb; o < oer; ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5336           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb*Nc + c + foffset;
5337           /* middle */
5338           for (i = 0; i < k-1; ++i) {
5339             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel+(k-2)-i)*Nc + c + foffset;
5340             for (o = of+(k-1)*i; o < of+(k-1)*(i+1); ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5341             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer+i)*Nc + c + foffset;
5342           }
5343           /* top */
5344           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt*Nc + c + foffset;
5345           for (o = oel-1; o >= oet; --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5346           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt*Nc + c + foffset;
5347           foffset = offset;
5348         }
5349         break;
5350       case 3:
5351         /* The original hex closure is
5352 
5353          {c,
5354          f_b, f_t, f_f, f_b, f_r, f_l,
5355          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5356          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5357          */
5358         PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5359         /* The SEM order is
5360          Bottom Slice
5361          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5362          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5363          v_blb, {e_bb}, v_brb,
5364 
5365          Middle Slice (j)
5366          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5367          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5368          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5369 
5370          Top Slice
5371          v_tlf, {e_tf}, v_trf,
5372          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5373          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5374          */
5375         {
5376           const PetscInt oc    = 0;
5377           const PetscInt ofb   = oc    + PetscSqr(k-1)*(k-1);
5378           const PetscInt oft   = ofb   + PetscSqr(k-1);
5379           const PetscInt off   = oft   + PetscSqr(k-1);
5380           const PetscInt ofk   = off   + PetscSqr(k-1);
5381           const PetscInt ofr   = ofk   + PetscSqr(k-1);
5382           const PetscInt ofl   = ofr   + PetscSqr(k-1);
5383           const PetscInt oebl  = ofl   + PetscSqr(k-1);
5384           const PetscInt oebb  = oebl  + (k-1);
5385           const PetscInt oebr  = oebb  + (k-1);
5386           const PetscInt oebf  = oebr  + (k-1);
5387           const PetscInt oetf  = oebf  + (k-1);
5388           const PetscInt oetr  = oetf  + (k-1);
5389           const PetscInt oetb  = oetr  + (k-1);
5390           const PetscInt oetl  = oetb  + (k-1);
5391           const PetscInt oerf  = oetl  + (k-1);
5392           const PetscInt oelf  = oerf  + (k-1);
5393           const PetscInt oelb  = oelf  + (k-1);
5394           const PetscInt oerb  = oelb  + (k-1);
5395           const PetscInt ovblf = oerb  + (k-1);
5396           const PetscInt ovblb = ovblf + 1;
5397           const PetscInt ovbrb = ovblb + 1;
5398           const PetscInt ovbrf = ovbrb + 1;
5399           const PetscInt ovtlf = ovbrf + 1;
5400           const PetscInt ovtrf = ovtlf + 1;
5401           const PetscInt ovtrb = ovtrf + 1;
5402           const PetscInt ovtlb = ovtrb + 1;
5403           PetscInt       o, n;
5404 
5405           /* Bottom Slice */
5406           /*   bottom */
5407           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf*Nc + c + foffset;
5408           for (o = oetf-1; o >= oebf; --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5409           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf*Nc + c + foffset;
5410           /*   middle */
5411           for (i = 0; i < k-1; ++i) {
5412             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl+i)*Nc + c + foffset;
5413             for (n = 0; n < k-1; ++n) {o = ofb+n*(k-1)+i; for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;}
5414             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr+(k-2)-i)*Nc + c + foffset;
5415           }
5416           /*   top */
5417           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb*Nc + c + foffset;
5418           for (o = oebb; o < oebr; ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5419           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb*Nc + c + foffset;
5420 
5421           /* Middle Slice */
5422           for (j = 0; j < k-1; ++j) {
5423             /*   bottom */
5424             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf+(k-2)-j)*Nc + c + foffset;
5425             for (o = off+j*(k-1); o < off+(j+1)*(k-1); ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5426             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf+j)*Nc + c + foffset;
5427             /*   middle */
5428             for (i = 0; i < k-1; ++i) {
5429               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl+i*(k-1)+j)*Nc + c + foffset;
5430               for (n = 0; n < k-1; ++n) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc+(j*(k-1)+i)*(k-1)+n)*Nc + c + foffset;
5431               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr+j*(k-1)+i)*Nc + c + foffset;
5432             }
5433             /*   top */
5434             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb+j)*Nc + c + foffset;
5435             for (o = ofk+j*(k-1)+(k-2); o >= ofk+j*(k-1); --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5436             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb+(k-2)-j)*Nc + c + foffset;
5437           }
5438 
5439           /* Top Slice */
5440           /*   bottom */
5441           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf*Nc + c + foffset;
5442           for (o = oetf; o < oetr; ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5443           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf*Nc + c + foffset;
5444           /*   middle */
5445           for (i = 0; i < k-1; ++i) {
5446             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl+(k-2)-i)*Nc + c + foffset;
5447             for (n = 0; n < k-1; ++n) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft+i*(k-1)+n)*Nc + c + foffset;
5448             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr+i)*Nc + c + foffset;
5449           }
5450           /*   top */
5451           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb*Nc + c + foffset;
5452           for (o = oetl-1; o >= oetb; --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5453           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb*Nc + c + foffset;
5454 
5455           foffset = offset;
5456         }
5457         break;
5458       default: SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
5459       }
5460     }
5461     PetscCheck(offset == size,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
5462     /* Check permutation */
5463     {
5464       PetscInt *check;
5465 
5466       PetscCall(PetscMalloc1(size, &check));
5467       for (i = 0; i < size; ++i) {
5468         check[i] = -1;
5469         PetscCheck(perm[i] >= 0 && perm[i] < size,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
5470       }
5471       for (i = 0; i < size; ++i) check[perm[i]] = i;
5472       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
5473       PetscCall(PetscFree(check));
5474     }
5475     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject) dm, d, size, PETSC_OWN_POINTER, perm));
5476     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
5477       PetscInt *loc_perm;
5478       PetscCall(PetscMalloc1(size*2, &loc_perm));
5479       for (PetscInt i=0; i<size; i++) {
5480         loc_perm[i] = perm[i];
5481         loc_perm[size+i] = size + perm[i];
5482       }
5483       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject) dm, d, size*2, PETSC_OWN_POINTER, loc_perm));
5484     }
5485   }
5486   PetscFunctionReturn(0);
5487 }
5488 
5489 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
5490 {
5491   PetscDS        prob;
5492   PetscInt       depth, Nf, h;
5493   DMLabel        label;
5494 
5495   PetscFunctionBeginHot;
5496   PetscCall(DMGetDS(dm, &prob));
5497   Nf      = prob->Nf;
5498   label   = dm->depthLabel;
5499   *dspace = NULL;
5500   if (field < Nf) {
5501     PetscObject disc = prob->disc[field];
5502 
5503     if (disc->classid == PETSCFE_CLASSID) {
5504       PetscDualSpace dsp;
5505 
5506       PetscCall(PetscFEGetDualSpace((PetscFE)disc,&dsp));
5507       PetscCall(DMLabelGetNumValues(label,&depth));
5508       PetscCall(DMLabelGetValue(label,point,&h));
5509       h    = depth - 1 - h;
5510       if (h) {
5511         PetscCall(PetscDualSpaceGetHeightSubspace(dsp,h,dspace));
5512       } else {
5513         *dspace = dsp;
5514       }
5515     }
5516   }
5517   PetscFunctionReturn(0);
5518 }
5519 
5520 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5521 {
5522   PetscScalar    *array, *vArray;
5523   const PetscInt *cone, *coneO;
5524   PetscInt        pStart, pEnd, p, numPoints, size = 0, offset = 0;
5525 
5526   PetscFunctionBeginHot;
5527   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5528   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
5529   PetscCall(DMPlexGetCone(dm, point, &cone));
5530   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
5531   if (!values || !*values) {
5532     if ((point >= pStart) && (point < pEnd)) {
5533       PetscInt dof;
5534 
5535       PetscCall(PetscSectionGetDof(section, point, &dof));
5536       size += dof;
5537     }
5538     for (p = 0; p < numPoints; ++p) {
5539       const PetscInt cp = cone[p];
5540       PetscInt       dof;
5541 
5542       if ((cp < pStart) || (cp >= pEnd)) continue;
5543       PetscCall(PetscSectionGetDof(section, cp, &dof));
5544       size += dof;
5545     }
5546     if (!values) {
5547       if (csize) *csize = size;
5548       PetscFunctionReturn(0);
5549     }
5550     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
5551   } else {
5552     array = *values;
5553   }
5554   size = 0;
5555   PetscCall(VecGetArray(v, &vArray));
5556   if ((point >= pStart) && (point < pEnd)) {
5557     PetscInt     dof, off, d;
5558     PetscScalar *varr;
5559 
5560     PetscCall(PetscSectionGetDof(section, point, &dof));
5561     PetscCall(PetscSectionGetOffset(section, point, &off));
5562     varr = &vArray[off];
5563     for (d = 0; d < dof; ++d, ++offset) {
5564       array[offset] = varr[d];
5565     }
5566     size += dof;
5567   }
5568   for (p = 0; p < numPoints; ++p) {
5569     const PetscInt cp = cone[p];
5570     PetscInt       o  = coneO[p];
5571     PetscInt       dof, off, d;
5572     PetscScalar   *varr;
5573 
5574     if ((cp < pStart) || (cp >= pEnd)) continue;
5575     PetscCall(PetscSectionGetDof(section, cp, &dof));
5576     PetscCall(PetscSectionGetOffset(section, cp, &off));
5577     varr = &vArray[off];
5578     if (o >= 0) {
5579       for (d = 0; d < dof; ++d, ++offset) {
5580         array[offset] = varr[d];
5581       }
5582     } else {
5583       for (d = dof-1; d >= 0; --d, ++offset) {
5584         array[offset] = varr[d];
5585       }
5586     }
5587     size += dof;
5588   }
5589   PetscCall(VecRestoreArray(v, &vArray));
5590   if (!*values) {
5591     if (csize) *csize = size;
5592     *values = array;
5593   } else {
5594     PetscCheck(size <= *csize,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5595     *csize = size;
5596   }
5597   PetscFunctionReturn(0);
5598 }
5599 
5600 /* Compress out points not in the section */
5601 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
5602 {
5603   const PetscInt np = *numPoints;
5604   PetscInt       pStart, pEnd, p, q;
5605 
5606   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5607   for (p = 0, q = 0; p < np; ++p) {
5608     const PetscInt r = points[p*2];
5609     if ((r >= pStart) && (r < pEnd)) {
5610       points[q*2]   = r;
5611       points[q*2+1] = points[p*2+1];
5612       ++q;
5613     }
5614   }
5615   *numPoints = q;
5616   return 0;
5617 }
5618 
5619 /* Compressed closure does not apply closure permutation */
5620 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5621 {
5622   const PetscInt *cla = NULL;
5623   PetscInt       np, *pts = NULL;
5624 
5625   PetscFunctionBeginHot;
5626   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject) dm, clSec, clPoints));
5627   if (*clPoints) {
5628     PetscInt dof, off;
5629 
5630     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
5631     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
5632     PetscCall(ISGetIndices(*clPoints, &cla));
5633     np   = dof/2;
5634     pts  = (PetscInt *) &cla[off];
5635   } else {
5636     PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &np, &pts));
5637     PetscCall(CompressPoints_Private(section, &np, pts));
5638   }
5639   *numPoints = np;
5640   *points    = pts;
5641   *clp       = cla;
5642   PetscFunctionReturn(0);
5643 }
5644 
5645 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5646 {
5647   PetscFunctionBeginHot;
5648   if (!*clPoints) {
5649     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
5650   } else {
5651     PetscCall(ISRestoreIndices(*clPoints, clp));
5652   }
5653   *numPoints = 0;
5654   *points    = NULL;
5655   *clSec     = NULL;
5656   *clPoints  = NULL;
5657   *clp       = NULL;
5658   PetscFunctionReturn(0);
5659 }
5660 
5661 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
5662 {
5663   PetscInt          offset = 0, p;
5664   const PetscInt    **perms = NULL;
5665   const PetscScalar **flips = NULL;
5666 
5667   PetscFunctionBeginHot;
5668   *size = 0;
5669   PetscCall(PetscSectionGetPointSyms(section,numPoints,points,&perms,&flips));
5670   for (p = 0; p < numPoints; p++) {
5671     const PetscInt    point = points[2*p];
5672     const PetscInt    *perm = perms ? perms[p] : NULL;
5673     const PetscScalar *flip = flips ? flips[p] : NULL;
5674     PetscInt          dof, off, d;
5675     const PetscScalar *varr;
5676 
5677     PetscCall(PetscSectionGetDof(section, point, &dof));
5678     PetscCall(PetscSectionGetOffset(section, point, &off));
5679     varr = &vArray[off];
5680     if (clperm) {
5681       if (perm) {
5682         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]]  = varr[d];
5683       } else {
5684         for (d = 0; d < dof; d++) array[clperm[offset +      d ]]  = varr[d];
5685       }
5686       if (flip) {
5687         for (d = 0; d < dof; d++) array[clperm[offset +      d ]] *= flip[d];
5688       }
5689     } else {
5690       if (perm) {
5691         for (d = 0; d < dof; d++) array[offset + perm[d]]  = varr[d];
5692       } else {
5693         for (d = 0; d < dof; d++) array[offset +      d ]  = varr[d];
5694       }
5695       if (flip) {
5696         for (d = 0; d < dof; d++) array[offset +      d ] *= flip[d];
5697       }
5698     }
5699     offset += dof;
5700   }
5701   PetscCall(PetscSectionRestorePointSyms(section,numPoints,points,&perms,&flips));
5702   *size = offset;
5703   PetscFunctionReturn(0);
5704 }
5705 
5706 static inline PetscErrorCode DMPlexVecGetClosure_Fields_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], PetscInt numFields, const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
5707 {
5708   PetscInt          offset = 0, f;
5709 
5710   PetscFunctionBeginHot;
5711   *size = 0;
5712   for (f = 0; f < numFields; ++f) {
5713     PetscInt          p;
5714     const PetscInt    **perms = NULL;
5715     const PetscScalar **flips = NULL;
5716 
5717     PetscCall(PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips));
5718     for (p = 0; p < numPoints; p++) {
5719       const PetscInt    point = points[2*p];
5720       PetscInt          fdof, foff, b;
5721       const PetscScalar *varr;
5722       const PetscInt    *perm = perms ? perms[p] : NULL;
5723       const PetscScalar *flip = flips ? flips[p] : NULL;
5724 
5725       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
5726       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
5727       varr = &vArray[foff];
5728       if (clperm) {
5729         if (perm) {for (b = 0; b < fdof; b++) {array[clperm[offset + perm[b]]]  = varr[b];}}
5730         else      {for (b = 0; b < fdof; b++) {array[clperm[offset +      b ]]  = varr[b];}}
5731         if (flip) {for (b = 0; b < fdof; b++) {array[clperm[offset +      b ]] *= flip[b];}}
5732       } else {
5733         if (perm) {for (b = 0; b < fdof; b++) {array[offset + perm[b]]  = varr[b];}}
5734         else      {for (b = 0; b < fdof; b++) {array[offset +      b ]  = varr[b];}}
5735         if (flip) {for (b = 0; b < fdof; b++) {array[offset +      b ] *= flip[b];}}
5736       }
5737       offset += fdof;
5738     }
5739     PetscCall(PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips));
5740   }
5741   *size = offset;
5742   PetscFunctionReturn(0);
5743 }
5744 
5745 /*@C
5746   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
5747 
5748   Not collective
5749 
5750   Input Parameters:
5751 + dm - The DM
5752 . section - The section describing the layout in v, or NULL to use the default section
5753 . v - The local vector
5754 - point - The point in the DM
5755 
5756   Input/Output Parameters:
5757 + csize  - The size of the input values array, or NULL; on output the number of values in the closure
5758 - values - An array to use for the values, or NULL to have it allocated automatically;
5759            if the user provided NULL, it is a borrowed array and should not be freed
5760 
5761 $ Note that DMPlexVecGetClosure/DMPlexVecRestoreClosure only allocates the values array if it set to NULL in the
5762 $ calling function. This is because DMPlexVecGetClosure() is typically called in the inner loop of a Vec or Mat
5763 $ assembly function, and a user may already have allocated storage for this operation.
5764 $
5765 $ A typical use could be
5766 $
5767 $  values = NULL;
5768 $  PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5769 $  for (cl = 0; cl < clSize; ++cl) {
5770 $    <Compute on closure>
5771 $  }
5772 $  PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
5773 $
5774 $ or
5775 $
5776 $  PetscMalloc1(clMaxSize, &values);
5777 $  for (p = pStart; p < pEnd; ++p) {
5778 $    clSize = clMaxSize;
5779 $    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5780 $    for (cl = 0; cl < clSize; ++cl) {
5781 $      <Compute on closure>
5782 $    }
5783 $  }
5784 $  PetscFree(values);
5785 
5786   Fortran Notes:
5787   Since it returns an array, this routine is only available in Fortran 90, and you must
5788   include petsc.h90 in your code.
5789 
5790   The csize argument is not present in the Fortran 90 binding since it is internal to the array.
5791 
5792   Level: intermediate
5793 
5794 .seealso `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
5795 @*/
5796 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5797 {
5798   PetscSection       clSection;
5799   IS                 clPoints;
5800   PetscInt          *points = NULL;
5801   const PetscInt    *clp, *perm;
5802   PetscInt           depth, numFields, numPoints, asize;
5803 
5804   PetscFunctionBeginHot;
5805   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5806   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5807   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5808   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5809   PetscCall(DMPlexGetDepth(dm, &depth));
5810   PetscCall(PetscSectionGetNumFields(section, &numFields));
5811   if (depth == 1 && numFields < 2) {
5812     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
5813     PetscFunctionReturn(0);
5814   }
5815   /* Get points */
5816   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5817   /* Get sizes */
5818   asize = 0;
5819   for (PetscInt p = 0; p < numPoints*2; p += 2) {
5820     PetscInt dof;
5821     PetscCall(PetscSectionGetDof(section, points[p], &dof));
5822     asize += dof;
5823   }
5824   if (values) {
5825     const PetscScalar *vArray;
5826     PetscInt          size;
5827 
5828     if (*values) {
5829       PetscCheck(*csize >= asize,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Provided array size %" PetscInt_FMT " not sufficient to hold closure size %" PetscInt_FMT, *csize, asize);
5830     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
5831     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, asize, &perm));
5832     PetscCall(VecGetArrayRead(v, &vArray));
5833     /* Get values */
5834     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
5835     else               PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
5836     PetscCheck(asize == size,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
5837     /* Cleanup array */
5838     PetscCall(VecRestoreArrayRead(v, &vArray));
5839   }
5840   if (csize) *csize = asize;
5841   /* Cleanup points */
5842   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5843   PetscFunctionReturn(0);
5844 }
5845 
5846 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
5847 {
5848   DMLabel            depthLabel;
5849   PetscSection       clSection;
5850   IS                 clPoints;
5851   PetscScalar       *array;
5852   const PetscScalar *vArray;
5853   PetscInt          *points = NULL;
5854   const PetscInt    *clp, *perm = NULL;
5855   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
5856 
5857   PetscFunctionBeginHot;
5858   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5859   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5860   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5861   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5862   PetscCall(DMPlexGetDepth(dm, &mdepth));
5863   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5864   PetscCall(PetscSectionGetNumFields(section, &numFields));
5865   if (mdepth == 1 && numFields < 2) {
5866     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
5867     PetscFunctionReturn(0);
5868   }
5869   /* Get points */
5870   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5871   for (clsize=0,p=0; p<Np; p++) {
5872     PetscInt dof;
5873     PetscCall(PetscSectionGetDof(section, points[2*p], &dof));
5874     clsize += dof;
5875   }
5876   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, clsize, &perm));
5877   /* Filter points */
5878   for (p = 0; p < numPoints*2; p += 2) {
5879     PetscInt dep;
5880 
5881     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
5882     if (dep != depth) continue;
5883     points[Np*2+0] = points[p];
5884     points[Np*2+1] = points[p+1];
5885     ++Np;
5886   }
5887   /* Get array */
5888   if (!values || !*values) {
5889     PetscInt asize = 0, dof;
5890 
5891     for (p = 0; p < Np*2; p += 2) {
5892       PetscCall(PetscSectionGetDof(section, points[p], &dof));
5893       asize += dof;
5894     }
5895     if (!values) {
5896       PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5897       if (csize) *csize = asize;
5898       PetscFunctionReturn(0);
5899     }
5900     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
5901   } else {
5902     array = *values;
5903   }
5904   PetscCall(VecGetArrayRead(v, &vArray));
5905   /* Get values */
5906   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
5907   else               PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
5908   /* Cleanup points */
5909   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5910   /* Cleanup array */
5911   PetscCall(VecRestoreArrayRead(v, &vArray));
5912   if (!*values) {
5913     if (csize) *csize = size;
5914     *values = array;
5915   } else {
5916     PetscCheck(size <= *csize,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5917     *csize = size;
5918   }
5919   PetscFunctionReturn(0);
5920 }
5921 
5922 /*@C
5923   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
5924 
5925   Not collective
5926 
5927   Input Parameters:
5928 + dm - The DM
5929 . section - The section describing the layout in v, or NULL to use the default section
5930 . v - The local vector
5931 . point - The point in the DM
5932 . csize - The number of values in the closure, or NULL
5933 - values - The array of values, which is a borrowed array and should not be freed
5934 
5935   Note that the array values are discarded and not copied back into v. In order to copy values back to v, use DMPlexVecSetClosure()
5936 
5937   Fortran Notes:
5938   Since it returns an array, this routine is only available in Fortran 90, and you must
5939   include petsc.h90 in your code.
5940 
5941   The csize argument is not present in the Fortran 90 binding since it is internal to the array.
5942 
5943   Level: intermediate
5944 
5945 .seealso `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
5946 @*/
5947 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5948 {
5949   PetscInt       size = 0;
5950 
5951   PetscFunctionBegin;
5952   /* Should work without recalculating size */
5953   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void*) values));
5954   *values = NULL;
5955   PetscFunctionReturn(0);
5956 }
5957 
5958 static inline void add   (PetscScalar *x, PetscScalar y) {*x += y;}
5959 static inline void insert(PetscScalar *x, PetscScalar y) {*x  = y;}
5960 
5961 static inline PetscErrorCode updatePoint_private(PetscSection section, PetscInt point, PetscInt dof, void (*fuse)(PetscScalar*, PetscScalar), PetscBool setBC, const PetscInt perm[], const PetscScalar flip[], const PetscInt clperm[], const PetscScalar values[], PetscInt offset, PetscScalar array[])
5962 {
5963   PetscInt        cdof;   /* The number of constraints on this point */
5964   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
5965   PetscScalar    *a;
5966   PetscInt        off, cind = 0, k;
5967 
5968   PetscFunctionBegin;
5969   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
5970   PetscCall(PetscSectionGetOffset(section, point, &off));
5971   a    = &array[off];
5972   if (!cdof || setBC) {
5973     if (clperm) {
5974       if (perm) {for (k = 0; k < dof; ++k) {fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));}}
5975       else      {for (k = 0; k < dof; ++k) {fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));}}
5976     } else {
5977       if (perm) {for (k = 0; k < dof; ++k) {fuse(&a[k], values[offset+perm[k]] * (flip ? flip[perm[k]] : 1.));}}
5978       else      {for (k = 0; k < dof; ++k) {fuse(&a[k], values[offset+     k ] * (flip ? flip[     k ] : 1.));}}
5979     }
5980   } else {
5981     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
5982     if (clperm) {
5983       if (perm) {for (k = 0; k < dof; ++k) {
5984           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5985           fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));
5986         }
5987       } else {
5988         for (k = 0; k < dof; ++k) {
5989           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5990           fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));
5991         }
5992       }
5993     } else {
5994       if (perm) {
5995         for (k = 0; k < dof; ++k) {
5996           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5997           fuse(&a[k], values[offset+perm[k]] * (flip ? flip[perm[k]] : 1.));
5998         }
5999       } else {
6000         for (k = 0; k < dof; ++k) {
6001           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
6002           fuse(&a[k], values[offset+     k ] * (flip ? flip[     k ] : 1.));
6003         }
6004       }
6005     }
6006   }
6007   PetscFunctionReturn(0);
6008 }
6009 
6010 static inline PetscErrorCode updatePointBC_private(PetscSection section, PetscInt point, PetscInt dof, void (*fuse)(PetscScalar*, PetscScalar), const PetscInt perm[], const PetscScalar flip[], const PetscInt clperm[], const PetscScalar values[], PetscInt offset, PetscScalar array[])
6011 {
6012   PetscInt        cdof;   /* The number of constraints on this point */
6013   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6014   PetscScalar    *a;
6015   PetscInt        off, cind = 0, k;
6016 
6017   PetscFunctionBegin;
6018   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6019   PetscCall(PetscSectionGetOffset(section, point, &off));
6020   a    = &array[off];
6021   if (cdof) {
6022     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6023     if (clperm) {
6024       if (perm) {
6025         for (k = 0; k < dof; ++k) {
6026           if ((cind < cdof) && (k == cdofs[cind])) {
6027             fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));
6028             cind++;
6029           }
6030         }
6031       } else {
6032         for (k = 0; k < dof; ++k) {
6033           if ((cind < cdof) && (k == cdofs[cind])) {
6034             fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));
6035             cind++;
6036           }
6037         }
6038       }
6039     } else {
6040       if (perm) {
6041         for (k = 0; k < dof; ++k) {
6042           if ((cind < cdof) && (k == cdofs[cind])) {
6043             fuse(&a[k], values[offset+perm[k]] * (flip ? flip[perm[k]] : 1.));
6044             cind++;
6045           }
6046         }
6047       } else {
6048         for (k = 0; k < dof; ++k) {
6049           if ((cind < cdof) && (k == cdofs[cind])) {
6050             fuse(&a[k], values[offset+     k ] * (flip ? flip[     k ] : 1.));
6051             cind++;
6052           }
6053         }
6054       }
6055     }
6056   }
6057   PetscFunctionReturn(0);
6058 }
6059 
6060 static inline PetscErrorCode updatePointFields_private(PetscSection section, PetscInt point, const PetscInt *perm, const PetscScalar *flip, PetscInt f, void (*fuse)(PetscScalar*, PetscScalar), PetscBool setBC, const PetscInt clperm[], const PetscScalar values[], PetscInt *offset, PetscScalar array[])
6061 {
6062   PetscScalar    *a;
6063   PetscInt        fdof, foff, fcdof, foffset = *offset;
6064   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6065   PetscInt        cind = 0, b;
6066 
6067   PetscFunctionBegin;
6068   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6069   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6070   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6071   a    = &array[foff];
6072   if (!fcdof || setBC) {
6073     if (clperm) {
6074       if (perm) {for (b = 0; b < fdof; b++) {fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));}}
6075       else      {for (b = 0; b < fdof; b++) {fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));}}
6076     } else {
6077       if (perm) {for (b = 0; b < fdof; b++) {fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));}}
6078       else      {for (b = 0; b < fdof; b++) {fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));}}
6079     }
6080   } else {
6081     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6082     if (clperm) {
6083       if (perm) {
6084         for (b = 0; b < fdof; b++) {
6085           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6086           fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));
6087         }
6088       } else {
6089         for (b = 0; b < fdof; b++) {
6090           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6091           fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));
6092         }
6093       }
6094     } else {
6095       if (perm) {
6096         for (b = 0; b < fdof; b++) {
6097           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6098           fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));
6099         }
6100       } else {
6101         for (b = 0; b < fdof; b++) {
6102           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6103           fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));
6104         }
6105       }
6106     }
6107   }
6108   *offset += fdof;
6109   PetscFunctionReturn(0);
6110 }
6111 
6112 static inline PetscErrorCode updatePointFieldsBC_private(PetscSection section, PetscInt point, const PetscInt perm[], const PetscScalar flip[], PetscInt f, PetscInt Ncc, const PetscInt comps[], void (*fuse)(PetscScalar*, PetscScalar), const PetscInt clperm[], const PetscScalar values[], PetscInt *offset, PetscScalar array[])
6113 {
6114   PetscScalar    *a;
6115   PetscInt        fdof, foff, fcdof, foffset = *offset;
6116   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6117   PetscInt        Nc, cind = 0, ncind = 0, b;
6118   PetscBool       ncSet, fcSet;
6119 
6120   PetscFunctionBegin;
6121   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6122   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6123   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6124   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6125   a    = &array[foff];
6126   if (fcdof) {
6127     /* We just override fcdof and fcdofs with Ncc and comps */
6128     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6129     if (clperm) {
6130       if (perm) {
6131         if (comps) {
6132           for (b = 0; b < fdof; b++) {
6133             ncSet = fcSet = PETSC_FALSE;
6134             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6135             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6136             if (ncSet && fcSet) {fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));}
6137           }
6138         } else {
6139           for (b = 0; b < fdof; b++) {
6140             if ((cind < fcdof) && (b == fcdofs[cind])) {
6141               fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));
6142               ++cind;
6143             }
6144           }
6145         }
6146       } else {
6147         if (comps) {
6148           for (b = 0; b < fdof; b++) {
6149             ncSet = fcSet = PETSC_FALSE;
6150             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6151             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6152             if (ncSet && fcSet) {fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));}
6153           }
6154         } else {
6155           for (b = 0; b < fdof; b++) {
6156             if ((cind < fcdof) && (b == fcdofs[cind])) {
6157               fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));
6158               ++cind;
6159             }
6160           }
6161         }
6162       }
6163     } else {
6164       if (perm) {
6165         if (comps) {
6166           for (b = 0; b < fdof; b++) {
6167             ncSet = fcSet = PETSC_FALSE;
6168             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6169             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6170             if (ncSet && fcSet) {fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));}
6171           }
6172         } else {
6173           for (b = 0; b < fdof; b++) {
6174             if ((cind < fcdof) && (b == fcdofs[cind])) {
6175               fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));
6176               ++cind;
6177             }
6178           }
6179         }
6180       } else {
6181         if (comps) {
6182           for (b = 0; b < fdof; b++) {
6183             ncSet = fcSet = PETSC_FALSE;
6184             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6185             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6186             if (ncSet && fcSet) {fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));}
6187           }
6188         } else {
6189           for (b = 0; b < fdof; b++) {
6190             if ((cind < fcdof) && (b == fcdofs[cind])) {
6191               fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));
6192               ++cind;
6193             }
6194           }
6195         }
6196       }
6197     }
6198   }
6199   *offset += fdof;
6200   PetscFunctionReturn(0);
6201 }
6202 
6203 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6204 {
6205   PetscScalar    *array;
6206   const PetscInt *cone, *coneO;
6207   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6208 
6209   PetscFunctionBeginHot;
6210   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6211   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6212   PetscCall(DMPlexGetCone(dm, point, &cone));
6213   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6214   PetscCall(VecGetArray(v, &array));
6215   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6216     const PetscInt cp = !p ? point : cone[p-1];
6217     const PetscInt o  = !p ? 0     : coneO[p-1];
6218 
6219     if ((cp < pStart) || (cp >= pEnd)) {dof = 0; continue;}
6220     PetscCall(PetscSectionGetDof(section, cp, &dof));
6221     /* ADD_VALUES */
6222     {
6223       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6224       PetscScalar    *a;
6225       PetscInt        cdof, coff, cind = 0, k;
6226 
6227       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6228       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6229       a    = &array[coff];
6230       if (!cdof) {
6231         if (o >= 0) {
6232           for (k = 0; k < dof; ++k) {
6233             a[k] += values[off+k];
6234           }
6235         } else {
6236           for (k = 0; k < dof; ++k) {
6237             a[k] += values[off+dof-k-1];
6238           }
6239         }
6240       } else {
6241         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6242         if (o >= 0) {
6243           for (k = 0; k < dof; ++k) {
6244             if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
6245             a[k] += values[off+k];
6246           }
6247         } else {
6248           for (k = 0; k < dof; ++k) {
6249             if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
6250             a[k] += values[off+dof-k-1];
6251           }
6252         }
6253       }
6254     }
6255   }
6256   PetscCall(VecRestoreArray(v, &array));
6257   PetscFunctionReturn(0);
6258 }
6259 
6260 /*@C
6261   DMPlexVecSetClosure - Set an array of the values on the closure of 'point'
6262 
6263   Not collective
6264 
6265   Input Parameters:
6266 + dm - The DM
6267 . section - The section describing the layout in v, or NULL to use the default section
6268 . v - The local vector
6269 . point - The point in the DM
6270 . values - The array of values
6271 - mode - The insert mode. One of INSERT_ALL_VALUES, ADD_ALL_VALUES, INSERT_VALUES, ADD_VALUES, INSERT_BC_VALUES, and ADD_BC_VALUES,
6272          where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions.
6273 
6274   Fortran Notes:
6275   This routine is only available in Fortran 90, and you must include petsc.h90 in your code.
6276 
6277   Level: intermediate
6278 
6279 .seealso `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6280 @*/
6281 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6282 {
6283   PetscSection    clSection;
6284   IS              clPoints;
6285   PetscScalar    *array;
6286   PetscInt       *points = NULL;
6287   const PetscInt *clp, *clperm = NULL;
6288   PetscInt        depth, numFields, numPoints, p, clsize;
6289 
6290   PetscFunctionBeginHot;
6291   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6292   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6293   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6294   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6295   PetscCall(DMPlexGetDepth(dm, &depth));
6296   PetscCall(PetscSectionGetNumFields(section, &numFields));
6297   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6298     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6299     PetscFunctionReturn(0);
6300   }
6301   /* Get points */
6302   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6303   for (clsize=0,p=0; p<numPoints; p++) {
6304     PetscInt dof;
6305     PetscCall(PetscSectionGetDof(section, points[2*p], &dof));
6306     clsize += dof;
6307   }
6308   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, clsize, &clperm));
6309   /* Get array */
6310   PetscCall(VecGetArray(v, &array));
6311   /* Get values */
6312   if (numFields > 0) {
6313     PetscInt offset = 0, f;
6314     for (f = 0; f < numFields; ++f) {
6315       const PetscInt    **perms = NULL;
6316       const PetscScalar **flips = NULL;
6317 
6318       PetscCall(PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6319       switch (mode) {
6320       case INSERT_VALUES:
6321         for (p = 0; p < numPoints; p++) {
6322           const PetscInt    point = points[2*p];
6323           const PetscInt    *perm = perms ? perms[p] : NULL;
6324           const PetscScalar *flip = flips ? flips[p] : NULL;
6325           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array);
6326         } break;
6327       case INSERT_ALL_VALUES:
6328         for (p = 0; p < numPoints; p++) {
6329           const PetscInt    point = points[2*p];
6330           const PetscInt    *perm = perms ? perms[p] : NULL;
6331           const PetscScalar *flip = flips ? flips[p] : NULL;
6332           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array);
6333         } break;
6334       case INSERT_BC_VALUES:
6335         for (p = 0; p < numPoints; p++) {
6336           const PetscInt    point = points[2*p];
6337           const PetscInt    *perm = perms ? perms[p] : NULL;
6338           const PetscScalar *flip = flips ? flips[p] : NULL;
6339           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array);
6340         } break;
6341       case ADD_VALUES:
6342         for (p = 0; p < numPoints; p++) {
6343           const PetscInt    point = points[2*p];
6344           const PetscInt    *perm = perms ? perms[p] : NULL;
6345           const PetscScalar *flip = flips ? flips[p] : NULL;
6346           updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array);
6347         } break;
6348       case ADD_ALL_VALUES:
6349         for (p = 0; p < numPoints; p++) {
6350           const PetscInt    point = points[2*p];
6351           const PetscInt    *perm = perms ? perms[p] : NULL;
6352           const PetscScalar *flip = flips ? flips[p] : NULL;
6353           updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array);
6354         } break;
6355       case ADD_BC_VALUES:
6356         for (p = 0; p < numPoints; p++) {
6357           const PetscInt    point = points[2*p];
6358           const PetscInt    *perm = perms ? perms[p] : NULL;
6359           const PetscScalar *flip = flips ? flips[p] : NULL;
6360           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array);
6361         } break;
6362       default:
6363         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6364       }
6365       PetscCall(PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6366     }
6367   } else {
6368     PetscInt dof, off;
6369     const PetscInt    **perms = NULL;
6370     const PetscScalar **flips = NULL;
6371 
6372     PetscCall(PetscSectionGetPointSyms(section,numPoints,points,&perms,&flips));
6373     switch (mode) {
6374     case INSERT_VALUES:
6375       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6376         const PetscInt    point = points[2*p];
6377         const PetscInt    *perm = perms ? perms[p] : NULL;
6378         const PetscScalar *flip = flips ? flips[p] : NULL;
6379         PetscCall(PetscSectionGetDof(section, point, &dof));
6380         updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array);
6381       } break;
6382     case INSERT_ALL_VALUES:
6383       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6384         const PetscInt    point = points[2*p];
6385         const PetscInt    *perm = perms ? perms[p] : NULL;
6386         const PetscScalar *flip = flips ? flips[p] : NULL;
6387         PetscCall(PetscSectionGetDof(section, point, &dof));
6388         updatePoint_private(section, point, dof, insert, PETSC_TRUE,  perm, flip, clperm, values, off, array);
6389       } break;
6390     case INSERT_BC_VALUES:
6391       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6392         const PetscInt    point = points[2*p];
6393         const PetscInt    *perm = perms ? perms[p] : NULL;
6394         const PetscScalar *flip = flips ? flips[p] : NULL;
6395         PetscCall(PetscSectionGetDof(section, point, &dof));
6396         updatePointBC_private(section, point, dof, insert,  perm, flip, clperm, values, off, array);
6397       } break;
6398     case ADD_VALUES:
6399       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6400         const PetscInt    point = points[2*p];
6401         const PetscInt    *perm = perms ? perms[p] : NULL;
6402         const PetscScalar *flip = flips ? flips[p] : NULL;
6403         PetscCall(PetscSectionGetDof(section, point, &dof));
6404         updatePoint_private(section, point, dof, add,    PETSC_FALSE, perm, flip, clperm, values, off, array);
6405       } break;
6406     case ADD_ALL_VALUES:
6407       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6408         const PetscInt    point = points[2*p];
6409         const PetscInt    *perm = perms ? perms[p] : NULL;
6410         const PetscScalar *flip = flips ? flips[p] : NULL;
6411         PetscCall(PetscSectionGetDof(section, point, &dof));
6412         updatePoint_private(section, point, dof, add,    PETSC_TRUE,  perm, flip, clperm, values, off, array);
6413       } break;
6414     case ADD_BC_VALUES:
6415       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6416         const PetscInt    point = points[2*p];
6417         const PetscInt    *perm = perms ? perms[p] : NULL;
6418         const PetscScalar *flip = flips ? flips[p] : NULL;
6419         PetscCall(PetscSectionGetDof(section, point, &dof));
6420         updatePointBC_private(section, point, dof, add,  perm, flip, clperm, values, off, array);
6421       } break;
6422     default:
6423       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6424     }
6425     PetscCall(PetscSectionRestorePointSyms(section,numPoints,points,&perms,&flips));
6426   }
6427   /* Cleanup points */
6428   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6429   /* Cleanup array */
6430   PetscCall(VecRestoreArray(v, &array));
6431   PetscFunctionReturn(0);
6432 }
6433 
6434 /* Check whether the given point is in the label. If not, update the offset to skip this point */
6435 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset)
6436 {
6437   PetscFunctionBegin;
6438   if (label) {
6439     PetscBool contains;
6440     PetscInt  fdof;
6441 
6442     PetscCall(DMLabelStratumHasPoint(label, labelId, point, &contains));
6443     if (!contains) {
6444       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6445       *offset += fdof;
6446       PetscFunctionReturn(1);
6447     }
6448   }
6449   PetscFunctionReturn(0);
6450 }
6451 
6452 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
6453 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)
6454 {
6455   PetscSection    clSection;
6456   IS              clPoints;
6457   PetscScalar    *array;
6458   PetscInt       *points = NULL;
6459   const PetscInt *clp;
6460   PetscInt        numFields, numPoints, p;
6461   PetscInt        offset = 0, f;
6462 
6463   PetscFunctionBeginHot;
6464   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6465   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6466   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6467   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6468   PetscCall(PetscSectionGetNumFields(section, &numFields));
6469   /* Get points */
6470   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6471   /* Get array */
6472   PetscCall(VecGetArray(v, &array));
6473   /* Get values */
6474   for (f = 0; f < numFields; ++f) {
6475     const PetscInt    **perms = NULL;
6476     const PetscScalar **flips = NULL;
6477 
6478     if (!fieldActive[f]) {
6479       for (p = 0; p < numPoints*2; p += 2) {
6480         PetscInt fdof;
6481         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
6482         offset += fdof;
6483       }
6484       continue;
6485     }
6486     PetscCall(PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6487     switch (mode) {
6488     case INSERT_VALUES:
6489       for (p = 0; p < numPoints; p++) {
6490         const PetscInt    point = points[2*p];
6491         const PetscInt    *perm = perms ? perms[p] : NULL;
6492         const PetscScalar *flip = flips ? flips[p] : NULL;
6493         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6494         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
6495       } break;
6496     case INSERT_ALL_VALUES:
6497       for (p = 0; p < numPoints; p++) {
6498         const PetscInt    point = points[2*p];
6499         const PetscInt    *perm = perms ? perms[p] : NULL;
6500         const PetscScalar *flip = flips ? flips[p] : NULL;
6501         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6502         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
6503       } break;
6504     case INSERT_BC_VALUES:
6505       for (p = 0; p < numPoints; p++) {
6506         const PetscInt    point = points[2*p];
6507         const PetscInt    *perm = perms ? perms[p] : NULL;
6508         const PetscScalar *flip = flips ? flips[p] : NULL;
6509         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6510         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
6511       } break;
6512     case ADD_VALUES:
6513       for (p = 0; p < numPoints; p++) {
6514         const PetscInt    point = points[2*p];
6515         const PetscInt    *perm = perms ? perms[p] : NULL;
6516         const PetscScalar *flip = flips ? flips[p] : NULL;
6517         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6518         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
6519       } break;
6520     case ADD_ALL_VALUES:
6521       for (p = 0; p < numPoints; p++) {
6522         const PetscInt    point = points[2*p];
6523         const PetscInt    *perm = perms ? perms[p] : NULL;
6524         const PetscScalar *flip = flips ? flips[p] : NULL;
6525         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6526         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
6527       } break;
6528     default:
6529       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6530     }
6531     PetscCall(PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6532   }
6533   /* Cleanup points */
6534   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6535   /* Cleanup array */
6536   PetscCall(VecRestoreArray(v, &array));
6537   PetscFunctionReturn(0);
6538 }
6539 
6540 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
6541 {
6542   PetscMPIInt    rank;
6543   PetscInt       i, j;
6544 
6545   PetscFunctionBegin;
6546   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
6547   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
6548   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
6549   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
6550   numCIndices = numCIndices ? numCIndices : numRIndices;
6551   if (!values) PetscFunctionReturn(0);
6552   for (i = 0; i < numRIndices; i++) {
6553     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
6554     for (j = 0; j < numCIndices; j++) {
6555 #if defined(PETSC_USE_COMPLEX)
6556       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i*numCIndices+j]), (double)PetscImaginaryPart(values[i*numCIndices+j])));
6557 #else
6558       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i*numCIndices+j]));
6559 #endif
6560     }
6561     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
6562   }
6563   PetscFunctionReturn(0);
6564 }
6565 
6566 /*
6567   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
6568 
6569   Input Parameters:
6570 + section - The section for this data layout
6571 . islocal - Is the section (and thus indices being requested) local or global?
6572 . point   - The point contributing dofs with these indices
6573 . off     - The global offset of this point
6574 . loff    - The local offset of each field
6575 . setBC   - The flag determining whether to include indices of boundary values
6576 . perm    - A permutation of the dofs on this point, or NULL
6577 - indperm - A permutation of the entire indices array, or NULL
6578 
6579   Output Parameter:
6580 . indices - Indices for dofs on this point
6581 
6582   Level: developer
6583 
6584   Note: The indices could be local or global, depending on the value of 'off'.
6585 */
6586 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal,PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
6587 {
6588   PetscInt        dof;   /* The number of unknowns on this point */
6589   PetscInt        cdof;  /* The number of constraints on this point */
6590   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6591   PetscInt        cind = 0, k;
6592 
6593   PetscFunctionBegin;
6594   PetscCheck(islocal || !setBC,PetscObjectComm((PetscObject)section),PETSC_ERR_ARG_INCOMP,"setBC incompatible with global indices; use a local section or disable setBC");
6595   PetscCall(PetscSectionGetDof(section, point, &dof));
6596   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6597   if (!cdof || setBC) {
6598     for (k = 0; k < dof; ++k) {
6599       const PetscInt preind = perm ? *loff+perm[k] : *loff+k;
6600       const PetscInt ind    = indperm ? indperm[preind] : preind;
6601 
6602       indices[ind] = off + k;
6603     }
6604   } else {
6605     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6606     for (k = 0; k < dof; ++k) {
6607       const PetscInt preind = perm ? *loff+perm[k] : *loff+k;
6608       const PetscInt ind    = indperm ? indperm[preind] : preind;
6609 
6610       if ((cind < cdof) && (k == cdofs[cind])) {
6611         /* Insert check for returning constrained indices */
6612         indices[ind] = -(off+k+1);
6613         ++cind;
6614       } else {
6615         indices[ind] = off + k - (islocal ? 0 : cind);
6616       }
6617     }
6618   }
6619   *loff += dof;
6620   PetscFunctionReturn(0);
6621 }
6622 
6623 /*
6624  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
6625 
6626  Input Parameters:
6627 + section - a section (global or local)
6628 - islocal - PETSC_TRUE if requesting local indices (i.e., section is local); PETSC_FALSE for global
6629 . point - point within section
6630 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
6631 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
6632 . setBC - identify constrained (boundary condition) points via involution.
6633 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
6634 . permsoff - offset
6635 - indperm - index permutation
6636 
6637  Output Parameter:
6638 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
6639 . indices - array to hold indices (as defined by section) of each dof associated with point
6640 
6641  Notes:
6642  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
6643  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
6644  in the local vector.
6645 
6646  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
6647  significant).  It is invalid to call with a global section and setBC=true.
6648 
6649  Developer Note:
6650  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
6651  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
6652  offset could be obtained from the section instead of passing it explicitly as we do now.
6653 
6654  Example:
6655  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
6656  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
6657  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
6658  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.
6659 
6660  Level: developer
6661 */
6662 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[])
6663 {
6664   PetscInt       numFields, foff, f;
6665 
6666   PetscFunctionBegin;
6667   PetscCheck(islocal || !setBC,PetscObjectComm((PetscObject)section),PETSC_ERR_ARG_INCOMP,"setBC incompatible with global indices; use a local section or disable setBC");
6668   PetscCall(PetscSectionGetNumFields(section, &numFields));
6669   for (f = 0, foff = 0; f < numFields; ++f) {
6670     PetscInt        fdof, cfdof;
6671     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6672     PetscInt        cind = 0, b;
6673     const PetscInt  *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6674 
6675     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6676     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
6677     if (!cfdof || setBC) {
6678       for (b = 0; b < fdof; ++b) {
6679         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6680         const PetscInt ind    = indperm ? indperm[preind] : preind;
6681 
6682         indices[ind] = off+foff+b;
6683       }
6684     } else {
6685       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6686       for (b = 0; b < fdof; ++b) {
6687         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6688         const PetscInt ind    = indperm ? indperm[preind] : preind;
6689 
6690         if ((cind < cfdof) && (b == fcdofs[cind])) {
6691           indices[ind] = -(off+foff+b+1);
6692           ++cind;
6693         } else {
6694           indices[ind] = off + foff + b - (islocal ? 0 : cind);
6695         }
6696       }
6697     }
6698     foff     += (setBC || islocal ? fdof : (fdof - cfdof));
6699     foffs[f] += fdof;
6700   }
6701   PetscFunctionReturn(0);
6702 }
6703 
6704 /*
6705   This version believes the globalSection offsets for each field, rather than just the point offset
6706 
6707  . foffs - The offset into 'indices' for each field, since it is segregated by field
6708 
6709  Notes:
6710  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
6711  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
6712 */
6713 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
6714 {
6715   PetscInt       numFields, foff, f;
6716 
6717   PetscFunctionBegin;
6718   PetscCall(PetscSectionGetNumFields(section, &numFields));
6719   for (f = 0; f < numFields; ++f) {
6720     PetscInt        fdof, cfdof;
6721     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6722     PetscInt        cind = 0, b;
6723     const PetscInt  *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6724 
6725     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6726     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
6727     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
6728     if (!cfdof) {
6729       for (b = 0; b < fdof; ++b) {
6730         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6731         const PetscInt ind    = indperm ? indperm[preind] : preind;
6732 
6733         indices[ind] = foff+b;
6734       }
6735     } else {
6736       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6737       for (b = 0; b < fdof; ++b) {
6738         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6739         const PetscInt ind    = indperm ? indperm[preind] : preind;
6740 
6741         if ((cind < cfdof) && (b == fcdofs[cind])) {
6742           indices[ind] = -(foff+b+1);
6743           ++cind;
6744         } else {
6745           indices[ind] = foff+b-cind;
6746         }
6747       }
6748     }
6749     foffs[f] += fdof;
6750   }
6751   PetscFunctionReturn(0);
6752 }
6753 
6754 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)
6755 {
6756   Mat             cMat;
6757   PetscSection    aSec, cSec;
6758   IS              aIS;
6759   PetscInt        aStart = -1, aEnd = -1;
6760   const PetscInt  *anchors;
6761   PetscInt        numFields, f, p, q, newP = 0;
6762   PetscInt        newNumPoints = 0, newNumIndices = 0;
6763   PetscInt        *newPoints, *indices, *newIndices;
6764   PetscInt        maxAnchor, maxDof;
6765   PetscInt        newOffsets[32];
6766   PetscInt        *pointMatOffsets[32];
6767   PetscInt        *newPointOffsets[32];
6768   PetscScalar     *pointMat[32];
6769   PetscScalar     *newValues=NULL,*tmpValues;
6770   PetscBool       anyConstrained = PETSC_FALSE;
6771 
6772   PetscFunctionBegin;
6773   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6774   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6775   PetscCall(PetscSectionGetNumFields(section, &numFields));
6776 
6777   PetscCall(DMPlexGetAnchors(dm,&aSec,&aIS));
6778   /* if there are point-to-point constraints */
6779   if (aSec) {
6780     PetscCall(PetscArrayzero(newOffsets, 32));
6781     PetscCall(ISGetIndices(aIS,&anchors));
6782     PetscCall(PetscSectionGetChart(aSec,&aStart,&aEnd));
6783     /* figure out how many points are going to be in the new element matrix
6784      * (we allow double counting, because it's all just going to be summed
6785      * into the global matrix anyway) */
6786     for (p = 0; p < 2*numPoints; p+=2) {
6787       PetscInt b    = points[p];
6788       PetscInt bDof = 0, bSecDof;
6789 
6790       PetscCall(PetscSectionGetDof(section,b,&bSecDof));
6791       if (!bSecDof) {
6792         continue;
6793       }
6794       if (b >= aStart && b < aEnd) {
6795         PetscCall(PetscSectionGetDof(aSec,b,&bDof));
6796       }
6797       if (bDof) {
6798         /* this point is constrained */
6799         /* it is going to be replaced by its anchors */
6800         PetscInt bOff, q;
6801 
6802         anyConstrained = PETSC_TRUE;
6803         newNumPoints  += bDof;
6804         PetscCall(PetscSectionGetOffset(aSec,b,&bOff));
6805         for (q = 0; q < bDof; q++) {
6806           PetscInt a = anchors[bOff + q];
6807           PetscInt aDof;
6808 
6809           PetscCall(PetscSectionGetDof(section,a,&aDof));
6810           newNumIndices += aDof;
6811           for (f = 0; f < numFields; ++f) {
6812             PetscInt fDof;
6813 
6814             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
6815             newOffsets[f+1] += fDof;
6816           }
6817         }
6818       }
6819       else {
6820         /* this point is not constrained */
6821         newNumPoints++;
6822         newNumIndices += bSecDof;
6823         for (f = 0; f < numFields; ++f) {
6824           PetscInt fDof;
6825 
6826           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6827           newOffsets[f+1] += fDof;
6828         }
6829       }
6830     }
6831   }
6832   if (!anyConstrained) {
6833     if (outNumPoints)  *outNumPoints  = 0;
6834     if (outNumIndices) *outNumIndices = 0;
6835     if (outPoints)     *outPoints     = NULL;
6836     if (outValues)     *outValues     = NULL;
6837     if (aSec) PetscCall(ISRestoreIndices(aIS,&anchors));
6838     PetscFunctionReturn(0);
6839   }
6840 
6841   if (outNumPoints)  *outNumPoints  = newNumPoints;
6842   if (outNumIndices) *outNumIndices = newNumIndices;
6843 
6844   for (f = 0; f < numFields; ++f) newOffsets[f+1] += newOffsets[f];
6845 
6846   if (!outPoints && !outValues) {
6847     if (offsets) {
6848       for (f = 0; f <= numFields; f++) {
6849         offsets[f] = newOffsets[f];
6850       }
6851     }
6852     if (aSec) PetscCall(ISRestoreIndices(aIS,&anchors));
6853     PetscFunctionReturn(0);
6854   }
6855 
6856   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
6857 
6858   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
6859 
6860   /* workspaces */
6861   if (numFields) {
6862     for (f = 0; f < numFields; f++) {
6863       PetscCall(DMGetWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[f]));
6864       PetscCall(DMGetWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[f]));
6865     }
6866   }
6867   else {
6868     PetscCall(DMGetWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[0]));
6869     PetscCall(DMGetWorkArray(dm,numPoints,MPIU_INT,&newPointOffsets[0]));
6870   }
6871 
6872   /* get workspaces for the point-to-point matrices */
6873   if (numFields) {
6874     PetscInt totalOffset, totalMatOffset;
6875 
6876     for (p = 0; p < numPoints; p++) {
6877       PetscInt b    = points[2*p];
6878       PetscInt bDof = 0, bSecDof;
6879 
6880       PetscCall(PetscSectionGetDof(section,b,&bSecDof));
6881       if (!bSecDof) {
6882         for (f = 0; f < numFields; f++) {
6883           newPointOffsets[f][p + 1] = 0;
6884           pointMatOffsets[f][p + 1] = 0;
6885         }
6886         continue;
6887       }
6888       if (b >= aStart && b < aEnd) {
6889         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6890       }
6891       if (bDof) {
6892         for (f = 0; f < numFields; f++) {
6893           PetscInt fDof, q, bOff, allFDof = 0;
6894 
6895           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6896           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6897           for (q = 0; q < bDof; q++) {
6898             PetscInt a = anchors[bOff + q];
6899             PetscInt aFDof;
6900 
6901             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
6902             allFDof += aFDof;
6903           }
6904           newPointOffsets[f][p+1] = allFDof;
6905           pointMatOffsets[f][p+1] = fDof * allFDof;
6906         }
6907       }
6908       else {
6909         for (f = 0; f < numFields; f++) {
6910           PetscInt fDof;
6911 
6912           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6913           newPointOffsets[f][p+1] = fDof;
6914           pointMatOffsets[f][p+1] = 0;
6915         }
6916       }
6917     }
6918     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
6919       newPointOffsets[f][0] = totalOffset;
6920       pointMatOffsets[f][0] = totalMatOffset;
6921       for (p = 0; p < numPoints; p++) {
6922         newPointOffsets[f][p+1] += newPointOffsets[f][p];
6923         pointMatOffsets[f][p+1] += pointMatOffsets[f][p];
6924       }
6925       totalOffset    = newPointOffsets[f][numPoints];
6926       totalMatOffset = pointMatOffsets[f][numPoints];
6927       PetscCall(DMGetWorkArray(dm,pointMatOffsets[f][numPoints],MPIU_SCALAR,&pointMat[f]));
6928     }
6929   }
6930   else {
6931     for (p = 0; p < numPoints; p++) {
6932       PetscInt b    = points[2*p];
6933       PetscInt bDof = 0, bSecDof;
6934 
6935       PetscCall(PetscSectionGetDof(section,b,&bSecDof));
6936       if (!bSecDof) {
6937         newPointOffsets[0][p + 1] = 0;
6938         pointMatOffsets[0][p + 1] = 0;
6939         continue;
6940       }
6941       if (b >= aStart && b < aEnd) {
6942         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6943       }
6944       if (bDof) {
6945         PetscInt bOff, q, allDof = 0;
6946 
6947         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6948         for (q = 0; q < bDof; q++) {
6949           PetscInt a = anchors[bOff + q], aDof;
6950 
6951           PetscCall(PetscSectionGetDof(section, a, &aDof));
6952           allDof += aDof;
6953         }
6954         newPointOffsets[0][p+1] = allDof;
6955         pointMatOffsets[0][p+1] = bSecDof * allDof;
6956       }
6957       else {
6958         newPointOffsets[0][p+1] = bSecDof;
6959         pointMatOffsets[0][p+1] = 0;
6960       }
6961     }
6962     newPointOffsets[0][0] = 0;
6963     pointMatOffsets[0][0] = 0;
6964     for (p = 0; p < numPoints; p++) {
6965       newPointOffsets[0][p+1] += newPointOffsets[0][p];
6966       pointMatOffsets[0][p+1] += pointMatOffsets[0][p];
6967     }
6968     PetscCall(DMGetWorkArray(dm,pointMatOffsets[0][numPoints],MPIU_SCALAR,&pointMat[0]));
6969   }
6970 
6971   /* output arrays */
6972   PetscCall(DMGetWorkArray(dm,2*newNumPoints,MPIU_INT,&newPoints));
6973 
6974   /* get the point-to-point matrices; construct newPoints */
6975   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
6976   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
6977   PetscCall(DMGetWorkArray(dm,maxDof,MPIU_INT,&indices));
6978   PetscCall(DMGetWorkArray(dm,maxAnchor*maxDof,MPIU_INT,&newIndices));
6979   if (numFields) {
6980     for (p = 0, newP = 0; p < numPoints; p++) {
6981       PetscInt b    = points[2*p];
6982       PetscInt o    = points[2*p+1];
6983       PetscInt bDof = 0, bSecDof;
6984 
6985       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
6986       if (!bSecDof) {
6987         continue;
6988       }
6989       if (b >= aStart && b < aEnd) {
6990         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6991       }
6992       if (bDof) {
6993         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
6994 
6995         fStart[0] = 0;
6996         fEnd[0]   = 0;
6997         for (f = 0; f < numFields; f++) {
6998           PetscInt fDof;
6999 
7000           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
7001           fStart[f+1] = fStart[f] + fDof;
7002           fEnd[f+1]   = fStart[f+1];
7003         }
7004         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7005         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7006 
7007         fAnchorStart[0] = 0;
7008         fAnchorEnd[0]   = 0;
7009         for (f = 0; f < numFields; f++) {
7010           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7011 
7012           fAnchorStart[f+1] = fAnchorStart[f] + fDof;
7013           fAnchorEnd[f+1]   = fAnchorStart[f + 1];
7014         }
7015         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7016         for (q = 0; q < bDof; q++) {
7017           PetscInt a = anchors[bOff + q], aOff;
7018 
7019           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7020           newPoints[2*(newP + q)]     = a;
7021           newPoints[2*(newP + q) + 1] = 0;
7022           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7023           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7024         }
7025         newP += bDof;
7026 
7027         if (outValues) {
7028           /* get the point-to-point submatrix */
7029           for (f = 0; f < numFields; f++) {
7030             PetscCall(MatGetValues(cMat,fEnd[f]-fStart[f],indices + fStart[f],fAnchorEnd[f] - fAnchorStart[f],newIndices + fAnchorStart[f],pointMat[f] + pointMatOffsets[f][p]));
7031           }
7032         }
7033       }
7034       else {
7035         newPoints[2 * newP]     = b;
7036         newPoints[2 * newP + 1] = o;
7037         newP++;
7038       }
7039     }
7040   } else {
7041     for (p = 0; p < numPoints; p++) {
7042       PetscInt b    = points[2*p];
7043       PetscInt o    = points[2*p+1];
7044       PetscInt bDof = 0, bSecDof;
7045 
7046       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7047       if (!bSecDof) {
7048         continue;
7049       }
7050       if (b >= aStart && b < aEnd) {
7051         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7052       }
7053       if (bDof) {
7054         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7055 
7056         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7057         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7058 
7059         PetscCall(PetscSectionGetOffset (aSec, b, &bOff));
7060         for (q = 0; q < bDof; q++) {
7061           PetscInt a = anchors[bOff + q], aOff;
7062 
7063           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7064 
7065           newPoints[2*(newP + q)]     = a;
7066           newPoints[2*(newP + q) + 1] = 0;
7067           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7068           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7069         }
7070         newP += bDof;
7071 
7072         /* get the point-to-point submatrix */
7073         if (outValues) {
7074           PetscCall(MatGetValues(cMat,bEnd,indices,bAnchorEnd,newIndices,pointMat[0] + pointMatOffsets[0][p]));
7075         }
7076       }
7077       else {
7078         newPoints[2 * newP]     = b;
7079         newPoints[2 * newP + 1] = o;
7080         newP++;
7081       }
7082     }
7083   }
7084 
7085   if (outValues) {
7086     PetscCall(DMGetWorkArray(dm,newNumIndices*numIndices,MPIU_SCALAR,&tmpValues));
7087     PetscCall(PetscArrayzero(tmpValues,newNumIndices*numIndices));
7088     /* multiply constraints on the right */
7089     if (numFields) {
7090       for (f = 0; f < numFields; f++) {
7091         PetscInt oldOff = offsets[f];
7092 
7093         for (p = 0; p < numPoints; p++) {
7094           PetscInt cStart = newPointOffsets[f][p];
7095           PetscInt b      = points[2 * p];
7096           PetscInt c, r, k;
7097           PetscInt dof;
7098 
7099           PetscCall(PetscSectionGetFieldDof(section,b,f,&dof));
7100           if (!dof) {
7101             continue;
7102           }
7103           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7104             PetscInt nCols         = newPointOffsets[f][p+1]-cStart;
7105             const PetscScalar *mat = pointMat[f] + pointMatOffsets[f][p];
7106 
7107             for (r = 0; r < numIndices; r++) {
7108               for (c = 0; c < nCols; c++) {
7109                 for (k = 0; k < dof; k++) {
7110                   tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7111                 }
7112               }
7113             }
7114           }
7115           else {
7116             /* copy this column as is */
7117             for (r = 0; r < numIndices; r++) {
7118               for (c = 0; c < dof; c++) {
7119                 tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7120               }
7121             }
7122           }
7123           oldOff += dof;
7124         }
7125       }
7126     }
7127     else {
7128       PetscInt oldOff = 0;
7129       for (p = 0; p < numPoints; p++) {
7130         PetscInt cStart = newPointOffsets[0][p];
7131         PetscInt b      = points[2 * p];
7132         PetscInt c, r, k;
7133         PetscInt dof;
7134 
7135         PetscCall(PetscSectionGetDof(section,b,&dof));
7136         if (!dof) {
7137           continue;
7138         }
7139         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7140           PetscInt nCols         = newPointOffsets[0][p+1]-cStart;
7141           const PetscScalar *mat = pointMat[0] + pointMatOffsets[0][p];
7142 
7143           for (r = 0; r < numIndices; r++) {
7144             for (c = 0; c < nCols; c++) {
7145               for (k = 0; k < dof; k++) {
7146                 tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7147               }
7148             }
7149           }
7150         }
7151         else {
7152           /* copy this column as is */
7153           for (r = 0; r < numIndices; r++) {
7154             for (c = 0; c < dof; c++) {
7155               tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7156             }
7157           }
7158         }
7159         oldOff += dof;
7160       }
7161     }
7162 
7163     if (multiplyLeft) {
7164       PetscCall(DMGetWorkArray(dm,newNumIndices*newNumIndices,MPIU_SCALAR,&newValues));
7165       PetscCall(PetscArrayzero(newValues,newNumIndices*newNumIndices));
7166       /* multiply constraints transpose on the left */
7167       if (numFields) {
7168         for (f = 0; f < numFields; f++) {
7169           PetscInt oldOff = offsets[f];
7170 
7171           for (p = 0; p < numPoints; p++) {
7172             PetscInt rStart = newPointOffsets[f][p];
7173             PetscInt b      = points[2 * p];
7174             PetscInt c, r, k;
7175             PetscInt dof;
7176 
7177             PetscCall(PetscSectionGetFieldDof(section,b,f,&dof));
7178             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7179               PetscInt nRows                        = newPointOffsets[f][p+1]-rStart;
7180               const PetscScalar *PETSC_RESTRICT mat = pointMat[f] + pointMatOffsets[f][p];
7181 
7182               for (r = 0; r < nRows; r++) {
7183                 for (c = 0; c < newNumIndices; c++) {
7184                   for (k = 0; k < dof; k++) {
7185                     newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7186                   }
7187                 }
7188               }
7189             }
7190             else {
7191               /* copy this row as is */
7192               for (r = 0; r < dof; r++) {
7193                 for (c = 0; c < newNumIndices; c++) {
7194                   newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7195                 }
7196               }
7197             }
7198             oldOff += dof;
7199           }
7200         }
7201       }
7202       else {
7203         PetscInt oldOff = 0;
7204 
7205         for (p = 0; p < numPoints; p++) {
7206           PetscInt rStart = newPointOffsets[0][p];
7207           PetscInt b      = points[2 * p];
7208           PetscInt c, r, k;
7209           PetscInt dof;
7210 
7211           PetscCall(PetscSectionGetDof(section,b,&dof));
7212           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7213             PetscInt nRows                        = newPointOffsets[0][p+1]-rStart;
7214             const PetscScalar *PETSC_RESTRICT mat = pointMat[0] + pointMatOffsets[0][p];
7215 
7216             for (r = 0; r < nRows; r++) {
7217               for (c = 0; c < newNumIndices; c++) {
7218                 for (k = 0; k < dof; k++) {
7219                   newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7220                 }
7221               }
7222             }
7223           }
7224           else {
7225             /* copy this row as is */
7226             for (r = 0; r < dof; r++) {
7227               for (c = 0; c < newNumIndices; c++) {
7228                 newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7229               }
7230             }
7231           }
7232           oldOff += dof;
7233         }
7234       }
7235 
7236       PetscCall(DMRestoreWorkArray(dm,newNumIndices*numIndices,MPIU_SCALAR,&tmpValues));
7237     }
7238     else {
7239       newValues = tmpValues;
7240     }
7241   }
7242 
7243   /* clean up */
7244   PetscCall(DMRestoreWorkArray(dm,maxDof,MPIU_INT,&indices));
7245   PetscCall(DMRestoreWorkArray(dm,maxAnchor*maxDof,MPIU_INT,&newIndices));
7246 
7247   if (numFields) {
7248     for (f = 0; f < numFields; f++) {
7249       PetscCall(DMRestoreWorkArray(dm,pointMatOffsets[f][numPoints],MPIU_SCALAR,&pointMat[f]));
7250       PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[f]));
7251       PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[f]));
7252     }
7253   }
7254   else {
7255     PetscCall(DMRestoreWorkArray(dm,pointMatOffsets[0][numPoints],MPIU_SCALAR,&pointMat[0]));
7256     PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[0]));
7257     PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[0]));
7258   }
7259   PetscCall(ISRestoreIndices(aIS,&anchors));
7260 
7261   /* output */
7262   if (outPoints) {
7263     *outPoints = newPoints;
7264   }
7265   else {
7266     PetscCall(DMRestoreWorkArray(dm,2*newNumPoints,MPIU_INT,&newPoints));
7267   }
7268   if (outValues) {
7269     *outValues = newValues;
7270   }
7271   for (f = 0; f <= numFields; f++) {
7272     offsets[f] = newOffsets[f];
7273   }
7274   PetscFunctionReturn(0);
7275 }
7276 
7277 /*@C
7278   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7279 
7280   Not collective
7281 
7282   Input Parameters:
7283 + dm         - The DM
7284 . section    - The PetscSection describing the points (a local section)
7285 . idxSection - The PetscSection from which to obtain indices (may be local or global)
7286 . point      - The point defining the closure
7287 - useClPerm  - Use the closure point permutation if available
7288 
7289   Output Parameters:
7290 + numIndices - The number of dof indices in the closure of point with the input sections
7291 . indices    - The dof indices
7292 . outOffsets - Array to write the field offsets into, or NULL
7293 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7294 
7295   Notes:
7296   Must call DMPlexRestoreClosureIndices() to free allocated memory
7297 
7298   If idxSection is global, any constrained dofs (see DMAddBoundary(), for example) will get negative indices.  The value
7299   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7300   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7301   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7302   indices (with the above semantics) are implied.
7303 
7304   Level: advanced
7305 
7306 .seealso `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7307 @*/
7308 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm,
7309                                        PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7310 {
7311   /* Closure ordering */
7312   PetscSection        clSection;
7313   IS                  clPoints;
7314   const PetscInt     *clp;
7315   PetscInt           *points;
7316   const PetscInt     *clperm = NULL;
7317   /* Dof permutation and sign flips */
7318   const PetscInt    **perms[32] = {NULL};
7319   const PetscScalar **flips[32] = {NULL};
7320   PetscScalar        *valCopy   = NULL;
7321   /* Hanging node constraints */
7322   PetscInt           *pointsC = NULL;
7323   PetscScalar        *valuesC = NULL;
7324   PetscInt            NclC, NiC;
7325 
7326   PetscInt           *idx;
7327   PetscInt            Nf, Ncl, Ni = 0, offsets[32], p, f;
7328   PetscBool           isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7329 
7330   PetscFunctionBeginHot;
7331   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7332   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7333   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7334   if (numIndices) PetscValidIntPointer(numIndices, 6);
7335   if (indices)    PetscValidPointer(indices, 7);
7336   if (outOffsets) PetscValidIntPointer(outOffsets, 8);
7337   if (values)     PetscValidPointer(values, 9);
7338   PetscCall(PetscSectionGetNumFields(section, &Nf));
7339   PetscCheck(Nf <= 31,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7340   PetscCall(PetscArrayzero(offsets, 32));
7341   /* 1) Get points in closure */
7342   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7343   if (useClPerm) {
7344     PetscInt depth, clsize;
7345     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7346     for (clsize=0,p=0; p<Ncl; p++) {
7347       PetscInt dof;
7348       PetscCall(PetscSectionGetDof(section, points[2*p], &dof));
7349       clsize += dof;
7350     }
7351     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, clsize, &clperm));
7352   }
7353   /* 2) Get number of indices on these points and field offsets from section */
7354   for (p = 0; p < Ncl*2; p += 2) {
7355     PetscInt dof, fdof;
7356 
7357     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7358     for (f = 0; f < Nf; ++f) {
7359       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7360       offsets[f+1] += fdof;
7361     }
7362     Ni += dof;
7363   }
7364   for (f = 1; f < Nf; ++f) offsets[f+1] += offsets[f];
7365   PetscCheck(!Nf || offsets[Nf] == Ni,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7366   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7367   for (f = 0; f < PetscMax(1, Nf); ++f) {
7368     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7369     else    PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7370     /* may need to apply sign changes to the element matrix */
7371     if (values && flips[f]) {
7372       PetscInt foffset = offsets[f];
7373 
7374       for (p = 0; p < Ncl; ++p) {
7375         PetscInt           pnt  = points[2*p], fdof;
7376         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7377 
7378         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7379         else     PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7380         if (flip) {
7381           PetscInt i, j, k;
7382 
7383           if (!valCopy) {
7384             PetscCall(DMGetWorkArray(dm, Ni*Ni, MPIU_SCALAR, &valCopy));
7385             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7386             *values = valCopy;
7387           }
7388           for (i = 0; i < fdof; ++i) {
7389             PetscScalar fval = flip[i];
7390 
7391             for (k = 0; k < Ni; ++k) {
7392               valCopy[Ni * (foffset + i) + k] *= fval;
7393               valCopy[Ni * k + (foffset + i)] *= fval;
7394             }
7395           }
7396         }
7397         foffset += fdof;
7398       }
7399     }
7400   }
7401   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7402   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7403   if (NclC) {
7404     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni*Ni, MPIU_SCALAR, &valCopy));
7405     for (f = 0; f < PetscMax(1, Nf); ++f) {
7406       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7407       else    PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7408     }
7409     for (f = 0; f < PetscMax(1, Nf); ++f) {
7410       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7411       else    PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7412     }
7413     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7414     Ncl     = NclC;
7415     Ni      = NiC;
7416     points  = pointsC;
7417     if (values) *values = valuesC;
7418   }
7419   /* 5) Calculate indices */
7420   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7421   if (Nf) {
7422     PetscInt  idxOff;
7423     PetscBool useFieldOffsets;
7424 
7425     if (outOffsets) {for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];}
7426     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7427     if (useFieldOffsets) {
7428       for (p = 0; p < Ncl; ++p) {
7429         const PetscInt pnt = points[p*2];
7430 
7431         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7432       }
7433     } else {
7434       for (p = 0; p < Ncl; ++p) {
7435         const PetscInt pnt = points[p*2];
7436 
7437         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7438         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7439          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7440          * global section. */
7441         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff+1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7442       }
7443     }
7444   } else {
7445     PetscInt off = 0, idxOff;
7446 
7447     for (p = 0; p < Ncl; ++p) {
7448       const PetscInt  pnt  = points[p*2];
7449       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7450 
7451       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7452       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7453        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7454       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff+1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7455     }
7456   }
7457   /* 6) Cleanup */
7458   for (f = 0; f < PetscMax(1, Nf); ++f) {
7459     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7460     else    PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7461   }
7462   if (NclC) {
7463     PetscCall(DMRestoreWorkArray(dm, NclC*2, MPIU_INT, &pointsC));
7464   } else {
7465     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7466   }
7467 
7468   if (numIndices) *numIndices = Ni;
7469   if (indices)    *indices    = idx;
7470   PetscFunctionReturn(0);
7471 }
7472 
7473 /*@C
7474   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7475 
7476   Not collective
7477 
7478   Input Parameters:
7479 + dm         - The DM
7480 . section    - The PetscSection describing the points (a local section)
7481 . idxSection - The PetscSection from which to obtain indices (may be local or global)
7482 . point      - The point defining the closure
7483 - useClPerm  - Use the closure point permutation if available
7484 
7485   Output Parameters:
7486 + numIndices - The number of dof indices in the closure of point with the input sections
7487 . indices    - The dof indices
7488 . outOffsets - Array to write the field offsets into, or NULL
7489 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7490 
7491   Notes:
7492   If values were modified, the user is responsible for calling DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values).
7493 
7494   If idxSection is global, any constrained dofs (see DMAddBoundary(), for example) will get negative indices.  The value
7495   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7496   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7497   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7498   indices (with the above semantics) are implied.
7499 
7500   Level: advanced
7501 
7502 .seealso `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7503 @*/
7504 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm,
7505                                            PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7506 {
7507   PetscFunctionBegin;
7508   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7509   PetscValidPointer(indices, 7);
7510   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
7511   PetscFunctionReturn(0);
7512 }
7513 
7514 /*@C
7515   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
7516 
7517   Not collective
7518 
7519   Input Parameters:
7520 + dm - The DM
7521 . section - The section describing the layout in v, or NULL to use the default section
7522 . globalSection - The section describing the layout in v, or NULL to use the default global section
7523 . A - The matrix
7524 . point - The point in the DM
7525 . values - The array of values
7526 - mode - The insert mode, where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions
7527 
7528   Fortran Notes:
7529   This routine is only available in Fortran 90, and you must include petsc.h90 in your code.
7530 
7531   Level: intermediate
7532 
7533 .seealso `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7534 @*/
7535 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7536 {
7537   DM_Plex           *mesh = (DM_Plex*) dm->data;
7538   PetscInt          *indices;
7539   PetscInt           numIndices;
7540   const PetscScalar *valuesOrig = values;
7541   PetscErrorCode     ierr;
7542 
7543   PetscFunctionBegin;
7544   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7545   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7546   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7547   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
7548   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
7549   PetscValidHeaderSpecific(A, MAT_CLASSID, 4);
7550 
7551   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **) &values));
7552 
7553   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
7554   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7555   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
7556   if (ierr) {
7557     PetscMPIInt    rank;
7558 
7559     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7560     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7561     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
7562     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **) &values));
7563     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7564     SETERRQ(PetscObjectComm((PetscObject)dm),ierr,"Not possible to set matrix values");
7565   }
7566   if (mesh->printFEM > 1) {
7567     PetscInt i;
7568     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
7569     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
7570     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
7571   }
7572 
7573   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **) &values));
7574   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7575   PetscFunctionReturn(0);
7576 }
7577 
7578 /*@C
7579   DMPlexMatSetClosure - Set an array of the values on the closure of 'point' using a different row and column section
7580 
7581   Not collective
7582 
7583   Input Parameters:
7584 + dmRow - The DM for the row fields
7585 . sectionRow - The section describing the layout, or NULL to use the default section in dmRow
7586 . globalSectionRow - The section describing the layout, or NULL to use the default global section in dmRow
7587 . dmCol - The DM for the column fields
7588 . sectionCol - The section describing the layout, or NULL to use the default section in dmCol
7589 . globalSectionCol - The section describing the layout, or NULL to use the default global section in dmCol
7590 . A - The matrix
7591 . point - The point in the DMs
7592 . values - The array of values
7593 - mode - The insert mode, where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions
7594 
7595   Level: intermediate
7596 
7597 .seealso `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7598 @*/
7599 PetscErrorCode DMPlexMatSetClosureGeneral(DM dmRow, PetscSection sectionRow, PetscSection globalSectionRow, DM dmCol, PetscSection sectionCol, PetscSection globalSectionCol, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7600 {
7601   DM_Plex           *mesh = (DM_Plex*) dmRow->data;
7602   PetscInt          *indicesRow, *indicesCol;
7603   PetscInt           numIndicesRow, numIndicesCol;
7604   const PetscScalar *valuesOrig = values;
7605   PetscErrorCode     ierr;
7606 
7607   PetscFunctionBegin;
7608   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
7609   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
7610   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
7611   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
7612   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
7613   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 4);
7614   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
7615   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 5);
7616   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
7617   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 6);
7618   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7619 
7620   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values));
7621   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **) &values));
7622 
7623   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7624   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7625   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
7626   if (ierr) {
7627     PetscMPIInt    rank;
7628 
7629     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7630     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7631     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7632     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values));
7633     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **) &values));
7634     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7635   }
7636 
7637   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values));
7638   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **) &values));
7639   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7640   PetscFunctionReturn(0);
7641 }
7642 
7643 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7644 {
7645   DM_Plex        *mesh   = (DM_Plex*) dmf->data;
7646   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7647   PetscInt       *cpoints = NULL;
7648   PetscInt       *findices, *cindices;
7649   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7650   PetscInt        foffsets[32], coffsets[32];
7651   DMPolytopeType  ct;
7652   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7653   PetscErrorCode  ierr;
7654 
7655   PetscFunctionBegin;
7656   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7657   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7658   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7659   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7660   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7661   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7662   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7663   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7664   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7665   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7666   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7667   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7668   PetscCheck(numFields <= 31,PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7669   PetscCall(PetscArrayzero(foffsets, 32));
7670   PetscCall(PetscArrayzero(coffsets, 32));
7671   /* Column indices */
7672   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7673   maxFPoints = numCPoints;
7674   /* Compress out points not in the section */
7675   /*   TODO: Squeeze out points with 0 dof as well */
7676   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7677   for (p = 0, q = 0; p < numCPoints*2; p += 2) {
7678     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7679       cpoints[q*2]   = cpoints[p];
7680       cpoints[q*2+1] = cpoints[p+1];
7681       ++q;
7682     }
7683   }
7684   numCPoints = q;
7685   for (p = 0, numCIndices = 0; p < numCPoints*2; p += 2) {
7686     PetscInt fdof;
7687 
7688     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7689     if (!dof) continue;
7690     for (f = 0; f < numFields; ++f) {
7691       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7692       coffsets[f+1] += fdof;
7693     }
7694     numCIndices += dof;
7695   }
7696   for (f = 1; f < numFields; ++f) coffsets[f+1] += coffsets[f];
7697   /* Row indices */
7698   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7699   {
7700     DMPlexTransform tr;
7701     DMPolytopeType *rct;
7702     PetscInt       *rsize, *rcone, *rornt, Nt;
7703 
7704     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7705     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7706     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7707     numSubcells = rsize[Nt-1];
7708     PetscCall(DMPlexTransformDestroy(&tr));
7709   }
7710   PetscCall(DMGetWorkArray(dmf, maxFPoints*2*numSubcells, MPIU_INT, &ftotpoints));
7711   for (r = 0, q = 0; r < numSubcells; ++r) {
7712     /* TODO Map from coarse to fine cells */
7713     PetscCall(DMPlexGetTransitiveClosure(dmf, point*numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7714     /* Compress out points not in the section */
7715     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7716     for (p = 0; p < numFPoints*2; p += 2) {
7717       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7718         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7719         if (!dof) continue;
7720         for (s = 0; s < q; ++s) if (fpoints[p] == ftotpoints[s*2]) break;
7721         if (s < q) continue;
7722         ftotpoints[q*2]   = fpoints[p];
7723         ftotpoints[q*2+1] = fpoints[p+1];
7724         ++q;
7725       }
7726     }
7727     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7728   }
7729   numFPoints = q;
7730   for (p = 0, numFIndices = 0; p < numFPoints*2; p += 2) {
7731     PetscInt fdof;
7732 
7733     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
7734     if (!dof) continue;
7735     for (f = 0; f < numFields; ++f) {
7736       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
7737       foffsets[f+1] += fdof;
7738     }
7739     numFIndices += dof;
7740   }
7741   for (f = 1; f < numFields; ++f) foffsets[f+1] += foffsets[f];
7742 
7743   PetscCheck(!numFields || foffsets[numFields] == numFIndices,PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
7744   PetscCheck(!numFields || coffsets[numFields] == numCIndices,PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
7745   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7746   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7747   if (numFields) {
7748     const PetscInt **permsF[32] = {NULL};
7749     const PetscInt **permsC[32] = {NULL};
7750 
7751     for (f = 0; f < numFields; f++) {
7752       PetscCall(PetscSectionGetFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7753       PetscCall(PetscSectionGetFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7754     }
7755     for (p = 0; p < numFPoints; p++) {
7756       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7757       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
7758     }
7759     for (p = 0; p < numCPoints; p++) {
7760       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7761       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
7762     }
7763     for (f = 0; f < numFields; f++) {
7764       PetscCall(PetscSectionRestoreFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7765       PetscCall(PetscSectionRestoreFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7766     }
7767   } else {
7768     const PetscInt **permsF = NULL;
7769     const PetscInt **permsC = NULL;
7770 
7771     PetscCall(PetscSectionGetPointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7772     PetscCall(PetscSectionGetPointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7773     for (p = 0, off = 0; p < numFPoints; p++) {
7774       const PetscInt *perm = permsF ? permsF[p] : NULL;
7775 
7776       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7777       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
7778     }
7779     for (p = 0, off = 0; p < numCPoints; p++) {
7780       const PetscInt *perm = permsC ? permsC[p] : NULL;
7781 
7782       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7783       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
7784     }
7785     PetscCall(PetscSectionRestorePointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7786     PetscCall(PetscSectionRestorePointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7787   }
7788   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
7789   /* TODO: flips */
7790   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7791   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
7792   if (ierr) {
7793     PetscMPIInt    rank;
7794 
7795     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7796     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7797     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
7798     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7799     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7800   }
7801   PetscCall(DMRestoreWorkArray(dmf, numCPoints*2*4, MPIU_INT, &ftotpoints));
7802   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7803   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7804   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7805   PetscFunctionReturn(0);
7806 }
7807 
7808 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
7809 {
7810   PetscInt      *fpoints = NULL, *ftotpoints = NULL;
7811   PetscInt      *cpoints = NULL;
7812   PetscInt       foffsets[32], coffsets[32];
7813   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7814   DMPolytopeType ct;
7815   PetscInt       numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7816 
7817   PetscFunctionBegin;
7818   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7819   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7820   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7821   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7822   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7823   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7824   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7825   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7826   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7827   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7828   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7829   PetscCheck(numFields <= 31,PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7830   PetscCall(PetscArrayzero(foffsets, 32));
7831   PetscCall(PetscArrayzero(coffsets, 32));
7832   /* Column indices */
7833   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7834   maxFPoints = numCPoints;
7835   /* Compress out points not in the section */
7836   /*   TODO: Squeeze out points with 0 dof as well */
7837   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7838   for (p = 0, q = 0; p < numCPoints*2; p += 2) {
7839     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7840       cpoints[q*2]   = cpoints[p];
7841       cpoints[q*2+1] = cpoints[p+1];
7842       ++q;
7843     }
7844   }
7845   numCPoints = q;
7846   for (p = 0, numCIndices = 0; p < numCPoints*2; p += 2) {
7847     PetscInt fdof;
7848 
7849     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7850     if (!dof) continue;
7851     for (f = 0; f < numFields; ++f) {
7852       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7853       coffsets[f+1] += fdof;
7854     }
7855     numCIndices += dof;
7856   }
7857   for (f = 1; f < numFields; ++f) coffsets[f+1] += coffsets[f];
7858   /* Row indices */
7859   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7860   {
7861     DMPlexTransform tr;
7862     DMPolytopeType *rct;
7863     PetscInt       *rsize, *rcone, *rornt, Nt;
7864 
7865     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7866     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7867     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7868     numSubcells = rsize[Nt-1];
7869     PetscCall(DMPlexTransformDestroy(&tr));
7870   }
7871   PetscCall(DMGetWorkArray(dmf, maxFPoints*2*numSubcells, MPIU_INT, &ftotpoints));
7872   for (r = 0, q = 0; r < numSubcells; ++r) {
7873     /* TODO Map from coarse to fine cells */
7874     PetscCall(DMPlexGetTransitiveClosure(dmf, point*numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7875     /* Compress out points not in the section */
7876     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7877     for (p = 0; p < numFPoints*2; p += 2) {
7878       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7879         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7880         if (!dof) continue;
7881         for (s = 0; s < q; ++s) if (fpoints[p] == ftotpoints[s*2]) break;
7882         if (s < q) continue;
7883         ftotpoints[q*2]   = fpoints[p];
7884         ftotpoints[q*2+1] = fpoints[p+1];
7885         ++q;
7886       }
7887     }
7888     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7889   }
7890   numFPoints = q;
7891   for (p = 0, numFIndices = 0; p < numFPoints*2; p += 2) {
7892     PetscInt fdof;
7893 
7894     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
7895     if (!dof) continue;
7896     for (f = 0; f < numFields; ++f) {
7897       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
7898       foffsets[f+1] += fdof;
7899     }
7900     numFIndices += dof;
7901   }
7902   for (f = 1; f < numFields; ++f) foffsets[f+1] += foffsets[f];
7903 
7904   PetscCheck(!numFields || foffsets[numFields] == numFIndices,PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
7905   PetscCheck(!numFields || coffsets[numFields] == numCIndices,PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
7906   if (numFields) {
7907     const PetscInt **permsF[32] = {NULL};
7908     const PetscInt **permsC[32] = {NULL};
7909 
7910     for (f = 0; f < numFields; f++) {
7911       PetscCall(PetscSectionGetFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7912       PetscCall(PetscSectionGetFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7913     }
7914     for (p = 0; p < numFPoints; p++) {
7915       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7916       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
7917     }
7918     for (p = 0; p < numCPoints; p++) {
7919       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7920       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
7921     }
7922     for (f = 0; f < numFields; f++) {
7923       PetscCall(PetscSectionRestoreFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7924       PetscCall(PetscSectionRestoreFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7925     }
7926   } else {
7927     const PetscInt **permsF = NULL;
7928     const PetscInt **permsC = NULL;
7929 
7930     PetscCall(PetscSectionGetPointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7931     PetscCall(PetscSectionGetPointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7932     for (p = 0, off = 0; p < numFPoints; p++) {
7933       const PetscInt *perm = permsF ? permsF[p] : NULL;
7934 
7935       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7936       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
7937     }
7938     for (p = 0, off = 0; p < numCPoints; p++) {
7939       const PetscInt *perm = permsC ? permsC[p] : NULL;
7940 
7941       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7942       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
7943     }
7944     PetscCall(PetscSectionRestorePointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7945     PetscCall(PetscSectionRestorePointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7946   }
7947   PetscCall(DMRestoreWorkArray(dmf, numCPoints*2*4, MPIU_INT, &ftotpoints));
7948   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7949   PetscFunctionReturn(0);
7950 }
7951 
7952 /*@C
7953   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
7954 
7955   Input Parameter:
7956 . dm   - The DMPlex object
7957 
7958   Output Parameter:
7959 . cellHeight - The height of a cell
7960 
7961   Level: developer
7962 
7963 .seealso `DMPlexSetVTKCellHeight()`
7964 @*/
7965 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
7966 {
7967   DM_Plex *mesh = (DM_Plex*) dm->data;
7968 
7969   PetscFunctionBegin;
7970   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7971   PetscValidIntPointer(cellHeight, 2);
7972   *cellHeight = mesh->vtkCellHeight;
7973   PetscFunctionReturn(0);
7974 }
7975 
7976 /*@C
7977   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
7978 
7979   Input Parameters:
7980 + dm   - The DMPlex object
7981 - cellHeight - The height of a cell
7982 
7983   Level: developer
7984 
7985 .seealso `DMPlexGetVTKCellHeight()`
7986 @*/
7987 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
7988 {
7989   DM_Plex *mesh = (DM_Plex*) dm->data;
7990 
7991   PetscFunctionBegin;
7992   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7993   mesh->vtkCellHeight = cellHeight;
7994   PetscFunctionReturn(0);
7995 }
7996 
7997 /*@
7998   DMPlexGetGhostCellStratum - Get the range of cells which are used to enforce FV boundary conditions
7999 
8000   Input Parameter:
8001 . dm - The DMPlex object
8002 
8003   Output Parameters:
8004 + gcStart - The first ghost cell, or NULL
8005 - gcEnd   - The upper bound on ghost cells, or NULL
8006 
8007   Level: advanced
8008 
8009 .seealso `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
8010 @*/
8011 PetscErrorCode DMPlexGetGhostCellStratum(DM dm, PetscInt *gcStart, PetscInt *gcEnd)
8012 {
8013   DMLabel        ctLabel;
8014 
8015   PetscFunctionBegin;
8016   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8017   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
8018   PetscCall(DMLabelGetStratumBounds(ctLabel, DM_POLYTOPE_FV_GHOST, gcStart, gcEnd));
8019   PetscFunctionReturn(0);
8020 }
8021 
8022 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8023 {
8024   PetscSection   section, globalSection;
8025   PetscInt      *numbers, p;
8026 
8027   PetscFunctionBegin;
8028   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf));
8029   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8030   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8031   for (p = pStart; p < pEnd; ++p) {
8032     PetscCall(PetscSectionSetDof(section, p, 1));
8033   }
8034   PetscCall(PetscSectionSetUp(section));
8035   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8036   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8037   for (p = pStart; p < pEnd; ++p) {
8038     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p-pStart]));
8039     if (numbers[p-pStart] < 0) numbers[p-pStart] -= shift;
8040     else                       numbers[p-pStart] += shift;
8041   }
8042   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject) dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8043   if (globalSize) {
8044     PetscLayout layout;
8045     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject) dm), globalSection, &layout));
8046     PetscCall(PetscLayoutGetSize(layout, globalSize));
8047     PetscCall(PetscLayoutDestroy(&layout));
8048   }
8049   PetscCall(PetscSectionDestroy(&section));
8050   PetscCall(PetscSectionDestroy(&globalSection));
8051   PetscFunctionReturn(0);
8052 }
8053 
8054 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8055 {
8056   PetscInt       cellHeight, cStart, cEnd;
8057 
8058   PetscFunctionBegin;
8059   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8060   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8061   else               PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8062   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8063   PetscFunctionReturn(0);
8064 }
8065 
8066 /*@
8067   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8068 
8069   Input Parameter:
8070 . dm   - The DMPlex object
8071 
8072   Output Parameter:
8073 . globalCellNumbers - Global cell numbers for all cells on this process
8074 
8075   Level: developer
8076 
8077 .seealso `DMPlexGetVertexNumbering()`
8078 @*/
8079 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8080 {
8081   DM_Plex       *mesh = (DM_Plex*) dm->data;
8082 
8083   PetscFunctionBegin;
8084   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8085   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8086   *globalCellNumbers = mesh->globalCellNumbers;
8087   PetscFunctionReturn(0);
8088 }
8089 
8090 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8091 {
8092   PetscInt       vStart, vEnd;
8093 
8094   PetscFunctionBegin;
8095   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8096   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8097   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8098   PetscFunctionReturn(0);
8099 }
8100 
8101 /*@
8102   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8103 
8104   Input Parameter:
8105 . dm   - The DMPlex object
8106 
8107   Output Parameter:
8108 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8109 
8110   Level: developer
8111 
8112 .seealso `DMPlexGetCellNumbering()`
8113 @*/
8114 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8115 {
8116   DM_Plex       *mesh = (DM_Plex*) dm->data;
8117 
8118   PetscFunctionBegin;
8119   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8120   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8121   *globalVertexNumbers = mesh->globalVertexNumbers;
8122   PetscFunctionReturn(0);
8123 }
8124 
8125 /*@
8126   DMPlexCreatePointNumbering - Create a global numbering for all points on this process
8127 
8128   Input Parameter:
8129 . dm   - The DMPlex object
8130 
8131   Output Parameter:
8132 . globalPointNumbers - Global numbers for all points on this process
8133 
8134   Level: developer
8135 
8136 .seealso `DMPlexGetCellNumbering()`
8137 @*/
8138 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8139 {
8140   IS             nums[4];
8141   PetscInt       depths[4], gdepths[4], starts[4];
8142   PetscInt       depth, d, shift = 0;
8143 
8144   PetscFunctionBegin;
8145   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8146   PetscCall(DMPlexGetDepth(dm, &depth));
8147   /* For unstratified meshes use dim instead of depth */
8148   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8149   for (d = 0; d <= depth; ++d) {
8150     PetscInt end;
8151 
8152     depths[d] = depth-d;
8153     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8154     if (!(starts[d]-end)) { starts[d] = depths[d] = -1; }
8155   }
8156   PetscCall(PetscSortIntWithArray(depth+1, starts, depths));
8157   PetscCall(MPIU_Allreduce(depths, gdepths, depth+1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject) dm)));
8158   for (d = 0; d <= depth; ++d) {
8159     PetscCheck(starts[d] < 0 || depths[d] == gdepths[d],PETSC_COMM_SELF,PETSC_ERR_PLIB,"Expected depth %" PetscInt_FMT ", found %" PetscInt_FMT,depths[d],gdepths[d]);
8160   }
8161   for (d = 0; d <= depth; ++d) {
8162     PetscInt pStart, pEnd, gsize;
8163 
8164     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8165     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8166     shift += gsize;
8167   }
8168   PetscCall(ISConcatenate(PetscObjectComm((PetscObject) dm), depth+1, nums, globalPointNumbers));
8169   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8170   PetscFunctionReturn(0);
8171 }
8172 
8173 /*@
8174   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8175 
8176   Input Parameter:
8177 . dm - The DMPlex object
8178 
8179   Output Parameter:
8180 . ranks - The rank field
8181 
8182   Options Database Keys:
8183 . -dm_partition_view - Adds the rank field into the DM output from -dm_view using the same viewer
8184 
8185   Level: intermediate
8186 
8187 .seealso: `DMView()`
8188 @*/
8189 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8190 {
8191   DM             rdm;
8192   PetscFE        fe;
8193   PetscScalar   *r;
8194   PetscMPIInt    rank;
8195   DMPolytopeType ct;
8196   PetscInt       dim, cStart, cEnd, c;
8197   PetscBool      simplex;
8198 
8199   PetscFunctionBeginUser;
8200   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8201   PetscValidPointer(ranks, 2);
8202   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject) dm), &rank));
8203   PetscCall(DMClone(dm, &rdm));
8204   PetscCall(DMGetDimension(rdm, &dim));
8205   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8206   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8207   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct)+1 ? PETSC_TRUE : PETSC_FALSE;
8208   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8209   PetscCall(PetscObjectSetName((PetscObject) fe, "rank"));
8210   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject) fe));
8211   PetscCall(PetscFEDestroy(&fe));
8212   PetscCall(DMCreateDS(rdm));
8213   PetscCall(DMCreateGlobalVector(rdm, ranks));
8214   PetscCall(PetscObjectSetName((PetscObject) *ranks, "partition"));
8215   PetscCall(VecGetArray(*ranks, &r));
8216   for (c = cStart; c < cEnd; ++c) {
8217     PetscScalar *lr;
8218 
8219     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8220     if (lr) *lr = rank;
8221   }
8222   PetscCall(VecRestoreArray(*ranks, &r));
8223   PetscCall(DMDestroy(&rdm));
8224   PetscFunctionReturn(0);
8225 }
8226 
8227 /*@
8228   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8229 
8230   Input Parameters:
8231 + dm    - The DMPlex
8232 - label - The DMLabel
8233 
8234   Output Parameter:
8235 . val - The label value field
8236 
8237   Options Database Keys:
8238 . -dm_label_view - Adds the label value field into the DM output from -dm_view using the same viewer
8239 
8240   Level: intermediate
8241 
8242 .seealso: `DMView()`
8243 @*/
8244 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8245 {
8246   DM             rdm;
8247   PetscFE        fe;
8248   PetscScalar   *v;
8249   PetscInt       dim, cStart, cEnd, c;
8250 
8251   PetscFunctionBeginUser;
8252   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8253   PetscValidPointer(label, 2);
8254   PetscValidPointer(val, 3);
8255   PetscCall(DMClone(dm, &rdm));
8256   PetscCall(DMGetDimension(rdm, &dim));
8257   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject) rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8258   PetscCall(PetscObjectSetName((PetscObject) fe, "label_value"));
8259   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject) fe));
8260   PetscCall(PetscFEDestroy(&fe));
8261   PetscCall(DMCreateDS(rdm));
8262   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8263   PetscCall(DMCreateGlobalVector(rdm, val));
8264   PetscCall(PetscObjectSetName((PetscObject) *val, "label_value"));
8265   PetscCall(VecGetArray(*val, &v));
8266   for (c = cStart; c < cEnd; ++c) {
8267     PetscScalar *lv;
8268     PetscInt     cval;
8269 
8270     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8271     PetscCall(DMLabelGetValue(label, c, &cval));
8272     *lv = cval;
8273   }
8274   PetscCall(VecRestoreArray(*val, &v));
8275   PetscCall(DMDestroy(&rdm));
8276   PetscFunctionReturn(0);
8277 }
8278 
8279 /*@
8280   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8281 
8282   Input Parameter:
8283 . dm - The DMPlex object
8284 
8285   Notes:
8286   This is a useful diagnostic when creating meshes programmatically.
8287 
8288   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8289 
8290   Level: developer
8291 
8292 .seealso: `DMCreate()`, `DMSetFromOptions()`
8293 @*/
8294 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8295 {
8296   PetscSection    coneSection, supportSection;
8297   const PetscInt *cone, *support;
8298   PetscInt        coneSize, c, supportSize, s;
8299   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8300   PetscBool       storagecheck = PETSC_TRUE;
8301 
8302   PetscFunctionBegin;
8303   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8304   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8305   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8306   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8307   /* Check that point p is found in the support of its cone points, and vice versa */
8308   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8309   for (p = pStart; p < pEnd; ++p) {
8310     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8311     PetscCall(DMPlexGetCone(dm, p, &cone));
8312     for (c = 0; c < coneSize; ++c) {
8313       PetscBool dup = PETSC_FALSE;
8314       PetscInt  d;
8315       for (d = c-1; d >= 0; --d) {
8316         if (cone[c] == cone[d]) {dup = PETSC_TRUE; break;}
8317       }
8318       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8319       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8320       for (s = 0; s < supportSize; ++s) {
8321         if (support[s] == p) break;
8322       }
8323       if ((s >= supportSize) || (dup && (support[s+1] != p))) {
8324         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8325         for (s = 0; s < coneSize; ++s) {
8326           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8327         }
8328         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8329         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8330         for (s = 0; s < supportSize; ++s) {
8331           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8332         }
8333         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8334         PetscCheck(!dup,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not repeatedly found in support of repeated cone point %" PetscInt_FMT, p, cone[c]);
8335         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8336       }
8337     }
8338     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8339     if (p != pp) { storagecheck = PETSC_FALSE; continue; }
8340     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8341     PetscCall(DMPlexGetSupport(dm, p, &support));
8342     for (s = 0; s < supportSize; ++s) {
8343       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8344       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8345       for (c = 0; c < coneSize; ++c) {
8346         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8347         if (cone[c] != pp) { c = 0; break; }
8348         if (cone[c] == p) break;
8349       }
8350       if (c >= coneSize) {
8351         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8352         for (c = 0; c < supportSize; ++c) {
8353           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8354         }
8355         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8356         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8357         for (c = 0; c < coneSize; ++c) {
8358           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8359         }
8360         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8361         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8362       }
8363     }
8364   }
8365   if (storagecheck) {
8366     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8367     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8368     PetscCheck(csize == ssize,PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8369   }
8370   PetscFunctionReturn(0);
8371 }
8372 
8373 /*
8374   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.
8375 */
8376 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
8377 {
8378   DMPolytopeType  cct;
8379   PetscInt        ptpoints[4];
8380   const PetscInt *cone, *ccone, *ptcone;
8381   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8382 
8383   PetscFunctionBegin;
8384   *unsplit = 0;
8385   switch (ct) {
8386     case DM_POLYTOPE_POINT_PRISM_TENSOR:
8387       ptpoints[npt++] = c;
8388       break;
8389     case DM_POLYTOPE_SEG_PRISM_TENSOR:
8390       PetscCall(DMPlexGetCone(dm, c, &cone));
8391       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8392       for (cp = 0; cp < coneSize; ++cp) {
8393         PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8394         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8395       }
8396       break;
8397     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8398     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8399       PetscCall(DMPlexGetCone(dm, c, &cone));
8400       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8401       for (cp = 0; cp < coneSize; ++cp) {
8402         PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8403         PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8404         for (ccp = 0; ccp < cconeSize; ++ccp) {
8405           PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8406           if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8407             PetscInt p;
8408             for (p = 0; p < npt; ++p) if (ptpoints[p] == ccone[ccp]) break;
8409             if (p == npt) ptpoints[npt++] = ccone[ccp];
8410           }
8411         }
8412       }
8413       break;
8414     default: break;
8415   }
8416   for (pt = 0; pt < npt; ++pt) {
8417     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8418     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8419   }
8420   PetscFunctionReturn(0);
8421 }
8422 
8423 /*@
8424   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8425 
8426   Input Parameters:
8427 + dm - The DMPlex object
8428 - cellHeight - Normally 0
8429 
8430   Notes:
8431   This is a useful diagnostic when creating meshes programmatically.
8432   Currently applicable only to homogeneous simplex or tensor meshes.
8433 
8434   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8435 
8436   Level: developer
8437 
8438 .seealso: `DMCreate()`, `DMSetFromOptions()`
8439 @*/
8440 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
8441 {
8442   DMPlexInterpolatedFlag interp;
8443   DMPolytopeType         ct;
8444   PetscInt               vStart, vEnd, cStart, cEnd, c;
8445 
8446   PetscFunctionBegin;
8447   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8448   PetscCall(DMPlexIsInterpolated(dm, &interp));
8449   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8450   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8451   for (c = cStart; c < cEnd; ++c) {
8452     PetscInt *closure = NULL;
8453     PetscInt  coneSize, closureSize, cl, Nv = 0;
8454 
8455     PetscCall(DMPlexGetCellType(dm, c, &ct));
8456     PetscCheck((PetscInt) ct >= 0,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
8457     if (ct == DM_POLYTOPE_UNKNOWN) continue;
8458     if (interp == DMPLEX_INTERPOLATED_FULL) {
8459       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8460       PetscCheck(coneSize == DMPolytopeTypeGetConeSize(ct),PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " of type %s has cone size %" PetscInt_FMT " != %" PetscInt_FMT, c, DMPolytopeTypes[ct], coneSize, DMPolytopeTypeGetConeSize(ct));
8461     }
8462     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8463     for (cl = 0; cl < closureSize*2; cl += 2) {
8464       const PetscInt p = closure[cl];
8465       if ((p >= vStart) && (p < vEnd)) ++Nv;
8466     }
8467     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8468     /* Special Case: Tensor faces with identified vertices */
8469     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
8470       PetscInt unsplit;
8471 
8472       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8473       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
8474     }
8475     PetscCheck(Nv == DMPolytopeTypeGetNumVertices(ct),PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " of type %s has %" PetscInt_FMT " vertices != %" PetscInt_FMT, c, DMPolytopeTypes[ct], Nv, DMPolytopeTypeGetNumVertices(ct));
8476   }
8477   PetscFunctionReturn(0);
8478 }
8479 
8480 /*@
8481   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
8482 
8483   Collective
8484 
8485   Input Parameters:
8486 + dm - The DMPlex object
8487 - cellHeight - Normally 0
8488 
8489   Notes:
8490   This is a useful diagnostic when creating meshes programmatically.
8491   This routine is only relevant for meshes that are fully interpolated across all ranks.
8492   It will error out if a partially interpolated mesh is given on some rank.
8493   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
8494 
8495   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8496 
8497   Level: developer
8498 
8499 .seealso: `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
8500 @*/
8501 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
8502 {
8503   PetscInt       dim, depth, vStart, vEnd, cStart, cEnd, c, h;
8504   DMPlexInterpolatedFlag interpEnum;
8505 
8506   PetscFunctionBegin;
8507   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8508   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
8509   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(0);
8510   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
8511     PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported");
8512     PetscFunctionReturn(0);
8513   }
8514 
8515   PetscCall(DMGetDimension(dm, &dim));
8516   PetscCall(DMPlexGetDepth(dm, &depth));
8517   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8518   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
8519     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
8520     for (c = cStart; c < cEnd; ++c) {
8521       const PetscInt      *cone, *ornt, *faceSizes, *faces;
8522       const DMPolytopeType *faceTypes;
8523       DMPolytopeType        ct;
8524       PetscInt              numFaces, coneSize, f;
8525       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
8526 
8527       PetscCall(DMPlexGetCellType(dm, c, &ct));
8528       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8529       if (unsplit) continue;
8530       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8531       PetscCall(DMPlexGetCone(dm, c, &cone));
8532       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
8533       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8534       for (cl = 0; cl < closureSize*2; cl += 2) {
8535         const PetscInt p = closure[cl];
8536         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
8537       }
8538       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8539       PetscCheck(coneSize == numFaces,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " of type %s has %" PetscInt_FMT " faces but should have %" PetscInt_FMT, c, DMPolytopeTypes[ct], coneSize, numFaces);
8540       for (f = 0; f < numFaces; ++f) {
8541         DMPolytopeType fct;
8542         PetscInt       *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
8543 
8544         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
8545         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
8546         for (cl = 0; cl < fclosureSize*2; cl += 2) {
8547           const PetscInt p = fclosure[cl];
8548           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
8549         }
8550         PetscCheck(fnumCorners == faceSizes[f],PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " of type %s (cone idx %" PetscInt_FMT ") of cell %" PetscInt_FMT " of type %s has %" PetscInt_FMT " vertices but should have %" PetscInt_FMT, cone[f], DMPolytopeTypes[fct], f, c, DMPolytopeTypes[ct], fnumCorners, faceSizes[f]);
8551         for (v = 0; v < fnumCorners; ++v) {
8552           if (fclosure[v] != faces[fOff+v]) {
8553             PetscInt v1;
8554 
8555             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
8556             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
8557             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
8558             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff+v1]));
8559             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8560             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " of type %s (cone idx %" PetscInt_FMT ", ornt %" PetscInt_FMT ") of cell %" PetscInt_FMT " of type %s vertex %" PetscInt_FMT ", %" PetscInt_FMT " != %" PetscInt_FMT, cone[f], DMPolytopeTypes[fct], f, ornt[f], c, DMPolytopeTypes[ct], v, fclosure[v], faces[fOff+v]);
8561           }
8562         }
8563         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
8564         fOff += faceSizes[f];
8565       }
8566       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8567       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8568     }
8569   }
8570   PetscFunctionReturn(0);
8571 }
8572 
8573 /*@
8574   DMPlexCheckGeometry - Check the geometry of mesh cells
8575 
8576   Input Parameter:
8577 . dm - The DMPlex object
8578 
8579   Notes:
8580   This is a useful diagnostic when creating meshes programmatically.
8581 
8582   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8583 
8584   Level: developer
8585 
8586 .seealso: `DMCreate()`, `DMSetFromOptions()`
8587 @*/
8588 PetscErrorCode DMPlexCheckGeometry(DM dm)
8589 {
8590   Vec       coordinates;
8591   PetscReal detJ, J[9], refVol = 1.0;
8592   PetscReal vol;
8593   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
8594 
8595   PetscFunctionBegin;
8596   PetscCall(DMGetDimension(dm, &dim));
8597   PetscCall(DMGetCoordinateDim(dm, &dE));
8598   if (dim != dE) PetscFunctionReturn(0);
8599   PetscCall(DMPlexGetDepth(dm, &depth));
8600   for (d = 0; d < dim; ++d) refVol *= 2.0;
8601   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
8602   /* Make sure local coordinates are created, because that step is collective */
8603   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
8604   for (c = cStart; c < cEnd; ++c) {
8605     DMPolytopeType ct;
8606     PetscInt       unsplit;
8607     PetscBool      ignoreZeroVol = PETSC_FALSE;
8608 
8609     PetscCall(DMPlexGetCellType(dm, c, &ct));
8610     switch (ct) {
8611       case DM_POLYTOPE_SEG_PRISM_TENSOR:
8612       case DM_POLYTOPE_TRI_PRISM_TENSOR:
8613       case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8614         ignoreZeroVol = PETSC_TRUE; break;
8615       default: break;
8616     }
8617     switch (ct) {
8618       case DM_POLYTOPE_TRI_PRISM:
8619       case DM_POLYTOPE_TRI_PRISM_TENSOR:
8620       case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8621       case DM_POLYTOPE_PYRAMID:
8622         continue;
8623       default: break;
8624     }
8625     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8626     if (unsplit) continue;
8627     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
8628     PetscCheck(detJ >= -PETSC_SMALL && (detJ > 0.0 || ignoreZeroVol),PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " of type %s is inverted, |J| = %g", c, DMPolytopeTypes[ct], (double) detJ);
8629     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ*refVol)));
8630     /* This should work with periodicity since DG coordinates should be used */
8631     if (depth > 1) {
8632       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
8633       PetscCheck(vol >= -PETSC_SMALL && (vol > 0.0 || ignoreZeroVol),PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " of type %s is inverted, vol = %g", c, DMPolytopeTypes[ct], (double) vol);
8634       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double) vol));
8635     }
8636   }
8637   PetscFunctionReturn(0);
8638 }
8639 
8640 /*@
8641   DMPlexCheckPointSF - Check that several necessary conditions are met for the Point SF of this plex.
8642 
8643   Collective
8644 
8645   Input Parameters:
8646 + dm - The DMPlex object
8647 - pointSF - The Point SF, or NULL for Point SF attached to DM
8648 
8649   Notes:
8650   This is mainly intended for debugging/testing purposes.
8651 
8652   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8653 
8654   Level: developer
8655 
8656 .seealso: `DMGetPointSF()`, `DMSetFromOptions()`
8657 @*/
8658 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF)
8659 {
8660   PetscInt        l, nleaves, nroots, overlap;
8661   const PetscInt *locals;
8662   const PetscSFNode *remotes;
8663   PetscBool       distributed;
8664   MPI_Comm        comm;
8665   PetscMPIInt     rank;
8666 
8667   PetscFunctionBegin;
8668   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8669   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
8670   else         pointSF = dm->sf;
8671   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
8672   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
8673   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8674   {
8675     PetscMPIInt    mpiFlag;
8676 
8677     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF),&mpiFlag));
8678     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)",mpiFlag);
8679   }
8680   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
8681   PetscCall(DMPlexIsDistributed(dm, &distributed));
8682   if (!distributed) {
8683     PetscCheck(nroots < 0 || nleaves == 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Undistributed DMPlex cannot have non-empty PointSF (has %" PetscInt_FMT " roots, %" PetscInt_FMT " leaves)", nroots, nleaves);
8684     PetscFunctionReturn(0);
8685   }
8686   PetscCheck(nroots >= 0, comm, PETSC_ERR_ARG_WRONGSTATE, "This DMPlex is distributed but its PointSF has no graph set (has %" PetscInt_FMT " roots, %" PetscInt_FMT " leaves)", nroots, nleaves);
8687   PetscCall(DMPlexGetOverlap(dm, &overlap));
8688 
8689   /* Check SF graph is compatible with DMPlex chart */
8690   {
8691     PetscInt pStart, pEnd, maxLeaf;
8692 
8693     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8694     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
8695     PetscCheck(pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd-pStart, nroots);
8696     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
8697   }
8698 
8699   /* Check Point SF has no local points referenced */
8700   for (l = 0; l < nleaves; l++) {
8701     PetscAssert(remotes[l].rank != (PetscInt) rank, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains local point %" PetscInt_FMT " <- (%" PetscInt_FMT ",%" PetscInt_FMT ")", locals ? locals[l] : l, remotes[l].rank, remotes[l].index);
8702   }
8703 
8704   /* Check there are no cells in interface */
8705   if (!overlap) {
8706     PetscInt cellHeight, cStart, cEnd;
8707 
8708     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8709     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8710     for (l = 0; l < nleaves; ++l) {
8711       const PetscInt point = locals ? locals[l] : l;
8712 
8713       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
8714     }
8715   }
8716 
8717   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
8718   {
8719     const PetscInt *rootdegree;
8720 
8721     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
8722     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
8723     for (l = 0; l < nleaves; ++l) {
8724       const PetscInt  point = locals ? locals[l] : l;
8725       const PetscInt *cone;
8726       PetscInt        coneSize, c, idx;
8727 
8728       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
8729       PetscCall(DMPlexGetCone(dm, point, &cone));
8730       for (c = 0; c < coneSize; ++c) {
8731         if (!rootdegree[cone[c]]) {
8732           if (locals) {
8733             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
8734           } else {
8735             idx = (cone[c] < nleaves) ? cone[c] : -1;
8736           }
8737           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
8738         }
8739       }
8740     }
8741   }
8742   PetscFunctionReturn(0);
8743 }
8744 
8745 /*@
8746   DMPlexCheck - Perform various checks of Plex sanity
8747 
8748   Input Parameter:
8749 . dm - The DMPlex object
8750 
8751   Notes:
8752   This is a useful diagnostic when creating meshes programmatically.
8753 
8754   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8755 
8756   Currently does not include DMPlexCheckCellShape().
8757 
8758   Level: developer
8759 
8760 .seealso: DMCreate(), DMSetFromOptions()
8761 @*/
8762 PetscErrorCode DMPlexCheck(DM dm)
8763 {
8764   PetscInt cellHeight;
8765 
8766   PetscFunctionBegin;
8767   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8768   PetscCall(DMPlexCheckSymmetry(dm));
8769   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
8770   PetscCall(DMPlexCheckFaces(dm, cellHeight));
8771   PetscCall(DMPlexCheckGeometry(dm));
8772   PetscCall(DMPlexCheckPointSF(dm, NULL));
8773   PetscCall(DMPlexCheckInterfaceCones(dm));
8774   PetscFunctionReturn(0);
8775 }
8776 
8777 typedef struct cell_stats
8778 {
8779   PetscReal min, max, sum, squaresum;
8780   PetscInt  count;
8781 } cell_stats_t;
8782 
8783 static void MPIAPI cell_stats_reduce(void *a, void *b, int * len, MPI_Datatype *datatype)
8784 {
8785   PetscInt i, N = *len;
8786 
8787   for (i = 0; i < N; i++) {
8788     cell_stats_t *A = (cell_stats_t *) a;
8789     cell_stats_t *B = (cell_stats_t *) b;
8790 
8791     B->min = PetscMin(A->min,B->min);
8792     B->max = PetscMax(A->max,B->max);
8793     B->sum += A->sum;
8794     B->squaresum += A->squaresum;
8795     B->count += A->count;
8796   }
8797 }
8798 
8799 /*@
8800   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
8801 
8802   Collective on dm
8803 
8804   Input Parameters:
8805 + dm        - The DMPlex object
8806 . output    - If true, statistics will be displayed on stdout
8807 - condLimit - Display all cells above this condition number, or PETSC_DETERMINE for no cell output
8808 
8809   Notes:
8810   This is mainly intended for debugging/testing purposes.
8811 
8812   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8813 
8814   Level: developer
8815 
8816 .seealso: `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
8817 @*/
8818 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
8819 {
8820   DM             dmCoarse;
8821   cell_stats_t   stats, globalStats;
8822   MPI_Comm       comm = PetscObjectComm((PetscObject)dm);
8823   PetscReal      *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
8824   PetscReal      limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
8825   PetscInt       cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
8826   PetscMPIInt    rank,size;
8827 
8828   PetscFunctionBegin;
8829   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8830   stats.min   = PETSC_MAX_REAL;
8831   stats.max   = PETSC_MIN_REAL;
8832   stats.sum   = stats.squaresum = 0.;
8833   stats.count = 0;
8834 
8835   PetscCallMPI(MPI_Comm_size(comm, &size));
8836   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8837   PetscCall(DMGetCoordinateDim(dm,&cdim));
8838   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
8839   PetscCall(DMPlexGetSimplexOrBoxCells(dm,0,&cStart,&cEnd));
8840   PetscCall(DMPlexGetDepthStratum(dm,1,&eStart,&eEnd));
8841   for (c = cStart; c < cEnd; c++) {
8842     PetscInt  i;
8843     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
8844 
8845     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm,c,NULL,J,invJ,&detJ));
8846     PetscCheck(detJ >= 0.0,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
8847     for (i = 0; i < PetscSqr(cdim); ++i) {
8848       frobJ    += J[i] * J[i];
8849       frobInvJ += invJ[i] * invJ[i];
8850     }
8851     cond2 = frobJ * frobInvJ;
8852     cond  = PetscSqrtReal(cond2);
8853 
8854     stats.min        = PetscMin(stats.min,cond);
8855     stats.max        = PetscMax(stats.max,cond);
8856     stats.sum       += cond;
8857     stats.squaresum += cond2;
8858     stats.count++;
8859     if (output && cond > limit) {
8860       PetscSection coordSection;
8861       Vec          coordsLocal;
8862       PetscScalar *coords = NULL;
8863       PetscInt     Nv, d, clSize, cl, *closure = NULL;
8864 
8865       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
8866       PetscCall(DMGetCoordinateSection(dm, &coordSection));
8867       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
8868       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double) cond));
8869       for (i = 0; i < Nv/cdim; ++i) {
8870         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
8871         for (d = 0; d < cdim; ++d) {
8872           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
8873           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double) PetscRealPart(coords[i*cdim+d])));
8874         }
8875         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
8876       }
8877       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
8878       for (cl = 0; cl < clSize*2; cl += 2) {
8879         const PetscInt edge = closure[cl];
8880 
8881         if ((edge >= eStart) && (edge < eEnd)) {
8882           PetscReal len;
8883 
8884           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
8885           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double) len));
8886         }
8887       }
8888       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
8889       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
8890     }
8891   }
8892   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
8893 
8894   if (size > 1) {
8895     PetscMPIInt   blockLengths[2] = {4,1};
8896     MPI_Aint      blockOffsets[2] = {offsetof(cell_stats_t,min),offsetof(cell_stats_t,count)};
8897     MPI_Datatype  blockTypes[2]   = {MPIU_REAL,MPIU_INT}, statType;
8898     MPI_Op        statReduce;
8899 
8900     PetscCallMPI(MPI_Type_create_struct(2,blockLengths,blockOffsets,blockTypes,&statType));
8901     PetscCallMPI(MPI_Type_commit(&statType));
8902     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
8903     PetscCallMPI(MPI_Reduce(&stats,&globalStats,1,statType,statReduce,0,comm));
8904     PetscCallMPI(MPI_Op_free(&statReduce));
8905     PetscCallMPI(MPI_Type_free(&statType));
8906   } else {
8907     PetscCall(PetscArraycpy(&globalStats,&stats,1));
8908   }
8909   if (rank == 0) {
8910     count = globalStats.count;
8911     min   = globalStats.min;
8912     max   = globalStats.max;
8913     mean  = globalStats.sum / globalStats.count;
8914     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1),0)) : 0.0;
8915   }
8916 
8917   if (output) {
8918     PetscCall(PetscPrintf(comm,"Mesh with %" PetscInt_FMT " cells, shape condition numbers: min = %g, max = %g, mean = %g, stddev = %g\n", count, (double) min, (double) max, (double) mean, (double) stdev));
8919   }
8920   PetscCall(PetscFree2(J,invJ));
8921 
8922   PetscCall(DMGetCoarseDM(dm,&dmCoarse));
8923   if (dmCoarse) {
8924     PetscBool isplex;
8925 
8926     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse,DMPLEX,&isplex));
8927     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse,output,condLimit));
8928   }
8929   PetscFunctionReturn(0);
8930 }
8931 
8932 /*@
8933   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
8934   orthogonal quality below given tolerance.
8935 
8936   Collective on dm
8937 
8938   Input Parameters:
8939 + dm   - The DMPlex object
8940 . fv   - Optional PetscFV object for pre-computed cell/face centroid information
8941 - atol - [0, 1] Absolute tolerance for tagging cells.
8942 
8943   Output Parameters:
8944 + OrthQual      - Vec containing orthogonal quality per cell
8945 - OrthQualLabel - DMLabel tagging cells below atol with DM_ADAPT_REFINE
8946 
8947   Options Database Keys:
8948 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only PETSCVIEWERASCII is
8949 supported.
8950 - -dm_plex_orthogonal_quality_vec_view - view OrthQual vector.
8951 
8952   Notes:
8953   Orthogonal quality is given by the following formula:
8954 
8955   \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]
8956 
8957   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
8958   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
8959   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
8960   calculating the cosine of the angle between these vectors.
8961 
8962   Orthogonal quality ranges from 1 (best) to 0 (worst).
8963 
8964   This routine is mainly useful for FVM, however is not restricted to only FVM. The PetscFV object is optionally used to check for
8965   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
8966 
8967   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
8968 
8969   Level: intermediate
8970 
8971 .seealso: `DMPlexCheckCellShape()`, `DMCreateLabel()`
8972 @*/
8973 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
8974 {
8975   PetscInt                nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
8976   PetscInt                *idx;
8977   PetscScalar             *oqVals;
8978   const PetscScalar       *cellGeomArr, *faceGeomArr;
8979   PetscReal               *ci, *fi, *Ai;
8980   MPI_Comm                comm;
8981   Vec                     cellgeom, facegeom;
8982   DM                      dmFace, dmCell;
8983   IS                      glob;
8984   ISLocalToGlobalMapping  ltog;
8985   PetscViewer             vwr;
8986 
8987   PetscFunctionBegin;
8988   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8989   if (fv) {PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);}
8990   PetscValidPointer(OrthQual, 4);
8991   PetscCheck(atol >= 0.0 && atol <= 1.0,PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Absolute tolerance %g not in [0,1]",(double)atol);
8992   PetscCall(PetscObjectGetComm((PetscObject) dm, &comm));
8993   PetscCall(DMGetDimension(dm, &nc));
8994   PetscCheck(nc >= 2,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
8995   {
8996     DMPlexInterpolatedFlag interpFlag;
8997 
8998     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
8999     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9000       PetscMPIInt rank;
9001 
9002       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9003       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9004     }
9005   }
9006   if (OrthQualLabel) {
9007     PetscValidPointer(OrthQualLabel, 5);
9008     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9009     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9010   } else {*OrthQualLabel = NULL;}
9011   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9012   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9013   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9014   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9015   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9016   PetscCall(VecCreate(comm, OrthQual));
9017   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9018   PetscCall(VecSetSizes(*OrthQual, cEnd-cStart, PETSC_DETERMINE));
9019   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9020   PetscCall(VecSetUp(*OrthQual));
9021   PetscCall(ISDestroy(&glob));
9022   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9023   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9024   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9025   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9026   PetscCall(VecGetDM(cellgeom, &dmCell));
9027   PetscCall(VecGetDM(facegeom, &dmFace));
9028   PetscCall(PetscMalloc5(cEnd-cStart, &idx, cEnd-cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9029   for (cell = cStart; cell < cEnd; cellIter++,cell++) {
9030     PetscInt           cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9031     PetscInt           cellarr[2], *adj = NULL;
9032     PetscScalar        *cArr, *fArr;
9033     PetscReal          minvalc = 1.0, minvalf = 1.0;
9034     PetscFVCellGeom    *cg;
9035 
9036     idx[cellIter] = cell-cStart;
9037     cellarr[0] = cell;
9038     /* Make indexing into cellGeom easier */
9039     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9040     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9041     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9042     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9043     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++,cellneigh++) {
9044       PetscInt         i;
9045       const PetscInt   neigh = adj[cellneigh];
9046       PetscReal        normci = 0, normfi = 0, normai = 0;
9047       PetscFVCellGeom  *cgneigh;
9048       PetscFVFaceGeom  *fg;
9049 
9050       /* Don't count ourselves in the neighbor list */
9051       if (neigh == cell) continue;
9052       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9053       cellarr[1] = neigh;
9054       {
9055         PetscInt       numcovpts;
9056         const PetscInt *covpts;
9057 
9058         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9059         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9060         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9061       }
9062 
9063       /* Compute c_i, f_i and their norms */
9064       for (i = 0; i < nc; i++) {
9065         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9066         fi[i] = fg->centroid[i] - cg->centroid[i];
9067         Ai[i] = fg->normal[i];
9068         normci += PetscPowReal(ci[i], 2);
9069         normfi += PetscPowReal(fi[i], 2);
9070         normai += PetscPowReal(Ai[i], 2);
9071       }
9072       normci = PetscSqrtReal(normci);
9073       normfi = PetscSqrtReal(normfi);
9074       normai = PetscSqrtReal(normai);
9075 
9076       /* Normalize and compute for each face-cell-normal pair */
9077       for (i = 0; i < nc; i++) {
9078         ci[i] = ci[i]/normci;
9079         fi[i] = fi[i]/normfi;
9080         Ai[i] = Ai[i]/normai;
9081         /* PetscAbs because I don't know if normals are guaranteed to point out */
9082         cArr[cellneighiter] += PetscAbs(Ai[i]*ci[i]);
9083         fArr[cellneighiter] += PetscAbs(Ai[i]*fi[i]);
9084       }
9085       if (PetscRealPart(cArr[cellneighiter]) < minvalc) {
9086         minvalc = PetscRealPart(cArr[cellneighiter]);
9087       }
9088       if (PetscRealPart(fArr[cellneighiter]) < minvalf) {
9089         minvalf = PetscRealPart(fArr[cellneighiter]);
9090       }
9091     }
9092     PetscCall(PetscFree(adj));
9093     PetscCall(PetscFree2(cArr, fArr));
9094     /* Defer to cell if they're equal */
9095     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9096     if (OrthQualLabel) {
9097       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9098     }
9099   }
9100   PetscCall(VecSetValuesLocal(*OrthQual, cEnd-cStart, idx, oqVals, INSERT_VALUES));
9101   PetscCall(VecAssemblyBegin(*OrthQual));
9102   PetscCall(VecAssemblyEnd(*OrthQual));
9103   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9104   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9105   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9106   if (OrthQualLabel) {
9107     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9108   }
9109   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9110   PetscCall(PetscViewerDestroy(&vwr));
9111   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9112   PetscFunctionReturn(0);
9113 }
9114 
9115 /* this is here insead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9116  * interpolator construction */
9117 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9118 {
9119   PetscSection   section, newSection, gsection;
9120   PetscSF        sf;
9121   PetscBool      hasConstraints, ghasConstraints;
9122 
9123   PetscFunctionBegin;
9124   PetscValidHeaderSpecific(dm,DM_CLASSID,1);
9125   PetscValidPointer(odm,2);
9126   PetscCall(DMGetLocalSection(dm, &section));
9127   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9128   PetscCallMPI(MPI_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject) dm)));
9129   if (!ghasConstraints) {
9130     PetscCall(PetscObjectReference((PetscObject)dm));
9131     *odm = dm;
9132     PetscFunctionReturn(0);
9133   }
9134   PetscCall(DMClone(dm, odm));
9135   PetscCall(DMCopyFields(dm, *odm));
9136   PetscCall(DMGetLocalSection(*odm, &newSection));
9137   PetscCall(DMGetPointSF(*odm, &sf));
9138   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9139   PetscCall(DMSetGlobalSection(*odm, gsection));
9140   PetscCall(PetscSectionDestroy(&gsection));
9141   PetscFunctionReturn(0);
9142 }
9143 
9144 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9145 {
9146   DM             dmco, dmfo;
9147   Mat            interpo;
9148   Vec            rscale;
9149   Vec            cglobalo, clocal;
9150   Vec            fglobal, fglobalo, flocal;
9151   PetscBool      regular;
9152 
9153   PetscFunctionBegin;
9154   PetscCall(DMGetFullDM(dmc, &dmco));
9155   PetscCall(DMGetFullDM(dmf, &dmfo));
9156   PetscCall(DMSetCoarseDM(dmfo, dmco));
9157   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9158   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9159   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9160   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9161   PetscCall(DMCreateLocalVector(dmc, &clocal));
9162   PetscCall(VecSet(cglobalo, 0.));
9163   PetscCall(VecSet(clocal, 0.));
9164   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9165   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9166   PetscCall(DMCreateLocalVector(dmf, &flocal));
9167   PetscCall(VecSet(fglobal, 0.));
9168   PetscCall(VecSet(fglobalo, 0.));
9169   PetscCall(VecSet(flocal, 0.));
9170   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9171   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9172   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9173   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9174   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9175   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9176   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9177   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9178   *shift = fglobal;
9179   PetscCall(VecDestroy(&flocal));
9180   PetscCall(VecDestroy(&fglobalo));
9181   PetscCall(VecDestroy(&clocal));
9182   PetscCall(VecDestroy(&cglobalo));
9183   PetscCall(VecDestroy(&rscale));
9184   PetscCall(MatDestroy(&interpo));
9185   PetscCall(DMDestroy(&dmfo));
9186   PetscCall(DMDestroy(&dmco));
9187   PetscFunctionReturn(0);
9188 }
9189 
9190 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9191 {
9192   PetscObject    shifto;
9193   Vec            shift;
9194 
9195   PetscFunctionBegin;
9196   if (!interp) {
9197     Vec rscale;
9198 
9199     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9200     PetscCall(VecDestroy(&rscale));
9201   } else {
9202     PetscCall(PetscObjectReference((PetscObject)interp));
9203   }
9204   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9205   if (!shifto) {
9206     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9207     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject) shift));
9208     shifto = (PetscObject) shift;
9209     PetscCall(VecDestroy(&shift));
9210   }
9211   shift = (Vec) shifto;
9212   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9213   PetscCall(VecAXPY(fineSol, 1.0, shift));
9214   PetscCall(MatDestroy(&interp));
9215   PetscFunctionReturn(0);
9216 }
9217 
9218 /* Pointwise interpolation
9219      Just code FEM for now
9220      u^f = I u^c
9221      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9222      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9223      I_{ij} = psi^f_i phi^c_j
9224 */
9225 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9226 {
9227   PetscSection   gsc, gsf;
9228   PetscInt       m, n;
9229   void          *ctx;
9230   DM             cdm;
9231   PetscBool      regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9232 
9233   PetscFunctionBegin;
9234   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9235   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9236   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9237   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9238 
9239   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9240   PetscCall(MatCreate(PetscObjectComm((PetscObject) dmCoarse), interpolation));
9241   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9242   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9243   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9244 
9245   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9246   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9247   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9248   else                                            PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9249   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9250   if (scaling) {
9251     /* Use naive scaling */
9252     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9253   }
9254   PetscFunctionReturn(0);
9255 }
9256 
9257 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9258 {
9259   VecScatter     ctx;
9260 
9261   PetscFunctionBegin;
9262   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9263   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9264   PetscCall(VecScatterDestroy(&ctx));
9265   PetscFunctionReturn(0);
9266 }
9267 
9268 static void g0_identity_private(PetscInt dim, PetscInt Nf, PetscInt NfAux,
9269                                 const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[],
9270                                 const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[],
9271                                 PetscReal t, PetscReal u_tShift, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar g0[])
9272 {
9273   const PetscInt Nc = uOff[1] - uOff[0];
9274   PetscInt       c;
9275   for (c = 0; c < Nc; ++c) g0[c*Nc+c] = 1.0;
9276 }
9277 
9278 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9279 {
9280   DM             dmc;
9281   PetscDS        ds;
9282   Vec            ones, locmass;
9283   IS             cellIS;
9284   PetscFormKey   key;
9285   PetscInt       depth;
9286 
9287   PetscFunctionBegin;
9288   PetscCall(DMClone(dm, &dmc));
9289   PetscCall(DMCopyDisc(dm, dmc));
9290   PetscCall(DMGetDS(dmc, &ds));
9291   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9292   PetscCall(DMCreateGlobalVector(dmc, mass));
9293   PetscCall(DMGetLocalVector(dmc, &ones));
9294   PetscCall(DMGetLocalVector(dmc, &locmass));
9295   PetscCall(DMPlexGetDepth(dmc, &depth));
9296   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9297   PetscCall(VecSet(locmass, 0.0));
9298   PetscCall(VecSet(ones, 1.0));
9299   key.label = NULL;
9300   key.value = 0;
9301   key.field = 0;
9302   key.part  = 0;
9303   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9304   PetscCall(ISDestroy(&cellIS));
9305   PetscCall(VecSet(*mass, 0.0));
9306   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9307   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9308   PetscCall(DMRestoreLocalVector(dmc, &ones));
9309   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9310   PetscCall(DMDestroy(&dmc));
9311   PetscFunctionReturn(0);
9312 }
9313 
9314 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9315 {
9316   PetscSection   gsc, gsf;
9317   PetscInt       m, n;
9318   void          *ctx;
9319   DM             cdm;
9320   PetscBool      regular;
9321 
9322   PetscFunctionBegin;
9323   if (dmFine == dmCoarse) {
9324     DM            dmc;
9325     PetscDS       ds;
9326     PetscWeakForm wf;
9327     Vec           u;
9328     IS            cellIS;
9329     PetscFormKey  key;
9330     PetscInt      depth;
9331 
9332     PetscCall(DMClone(dmFine, &dmc));
9333     PetscCall(DMCopyDisc(dmFine, dmc));
9334     PetscCall(DMGetDS(dmc, &ds));
9335     PetscCall(PetscDSGetWeakForm(ds, &wf));
9336     PetscCall(PetscWeakFormClear(wf));
9337     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9338     PetscCall(DMCreateMatrix(dmc, mass));
9339     PetscCall(DMGetGlobalVector(dmc, &u));
9340     PetscCall(DMPlexGetDepth(dmc, &depth));
9341     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9342     PetscCall(MatZeroEntries(*mass));
9343     key.label = NULL;
9344     key.value = 0;
9345     key.field = 0;
9346     key.part  = 0;
9347     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9348     PetscCall(ISDestroy(&cellIS));
9349     PetscCall(DMRestoreGlobalVector(dmc, &u));
9350     PetscCall(DMDestroy(&dmc));
9351   } else {
9352     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9353     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9354     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9355     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9356 
9357     PetscCall(MatCreate(PetscObjectComm((PetscObject) dmCoarse), mass));
9358     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9359     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9360     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9361 
9362     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9363     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9364     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9365     else                            PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9366   }
9367   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9368   PetscFunctionReturn(0);
9369 }
9370 
9371 /*@
9372   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9373 
9374   Input Parameter:
9375 . dm - The DMPlex object
9376 
9377   Output Parameter:
9378 . regular - The flag
9379 
9380   Level: intermediate
9381 
9382 .seealso: `DMPlexSetRegularRefinement()`
9383 @*/
9384 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
9385 {
9386   PetscFunctionBegin;
9387   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9388   PetscValidBoolPointer(regular, 2);
9389   *regular = ((DM_Plex *) dm->data)->regularRefinement;
9390   PetscFunctionReturn(0);
9391 }
9392 
9393 /*@
9394   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9395 
9396   Input Parameters:
9397 + dm - The DMPlex object
9398 - regular - The flag
9399 
9400   Level: intermediate
9401 
9402 .seealso: `DMPlexGetRegularRefinement()`
9403 @*/
9404 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
9405 {
9406   PetscFunctionBegin;
9407   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9408   ((DM_Plex *) dm->data)->regularRefinement = regular;
9409   PetscFunctionReturn(0);
9410 }
9411 
9412 /* anchors */
9413 /*@
9414   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9415   call DMPlexGetAnchors() directly: if there are anchors, then DMPlexGetAnchors() is called during DMGetDefaultConstraints().
9416 
9417   not collective
9418 
9419   Input Parameter:
9420 . dm - The DMPlex object
9421 
9422   Output Parameters:
9423 + anchorSection - If not NULL, set to the section describing which points anchor the constrained points.
9424 - anchorIS - If not NULL, set to the list of anchors indexed by anchorSection
9425 
9426   Level: intermediate
9427 
9428 .seealso: `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9429 @*/
9430 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
9431 {
9432   DM_Plex *plex = (DM_Plex *)dm->data;
9433 
9434   PetscFunctionBegin;
9435   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9436   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9437   if (anchorSection) *anchorSection = plex->anchorSection;
9438   if (anchorIS) *anchorIS = plex->anchorIS;
9439   PetscFunctionReturn(0);
9440 }
9441 
9442 /*@
9443   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.  Unlike boundary conditions,
9444   when a point's degrees of freedom in a section are constrained to an outside value, the anchor constraints set a
9445   point's degrees of freedom to be a linear combination of other points' degrees of freedom.
9446 
9447   After specifying the layout of constraints with DMPlexSetAnchors(), one specifies the constraints by calling
9448   DMGetDefaultConstraints() and filling in the entries in the constraint matrix.
9449 
9450   collective on dm
9451 
9452   Input Parameters:
9453 + dm - The DMPlex object
9454 . 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).
9455 - anchorIS - The list of all anchor points.  Must have a local communicator (PETSC_COMM_SELF or derivative).
9456 
9457   The reference counts of anchorSection and anchorIS are incremented.
9458 
9459   Level: intermediate
9460 
9461 .seealso: `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9462 @*/
9463 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
9464 {
9465   DM_Plex        *plex = (DM_Plex *)dm->data;
9466   PetscMPIInt    result;
9467 
9468   PetscFunctionBegin;
9469   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9470   if (anchorSection) {
9471     PetscValidHeaderSpecific(anchorSection,PETSC_SECTION_CLASSID,2);
9472     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF,PetscObjectComm((PetscObject)anchorSection),&result));
9473     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT,PETSC_COMM_SELF,PETSC_ERR_ARG_NOTSAMECOMM,"anchor section must have local communicator");
9474   }
9475   if (anchorIS) {
9476     PetscValidHeaderSpecific(anchorIS,IS_CLASSID,3);
9477     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF,PetscObjectComm((PetscObject)anchorIS),&result));
9478     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT,PETSC_COMM_SELF,PETSC_ERR_ARG_NOTSAMECOMM,"anchor IS must have local communicator");
9479   }
9480 
9481   PetscCall(PetscObjectReference((PetscObject)anchorSection));
9482   PetscCall(PetscSectionDestroy(&plex->anchorSection));
9483   plex->anchorSection = anchorSection;
9484 
9485   PetscCall(PetscObjectReference((PetscObject)anchorIS));
9486   PetscCall(ISDestroy(&plex->anchorIS));
9487   plex->anchorIS = anchorIS;
9488 
9489   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
9490     PetscInt size, a, pStart, pEnd;
9491     const PetscInt *anchors;
9492 
9493     PetscCall(PetscSectionGetChart(anchorSection,&pStart,&pEnd));
9494     PetscCall(ISGetLocalSize(anchorIS,&size));
9495     PetscCall(ISGetIndices(anchorIS,&anchors));
9496     for (a = 0; a < size; a++) {
9497       PetscInt p;
9498 
9499       p = anchors[a];
9500       if (p >= pStart && p < pEnd) {
9501         PetscInt dof;
9502 
9503         PetscCall(PetscSectionGetDof(anchorSection,p,&dof));
9504         if (dof) {
9505 
9506           PetscCall(ISRestoreIndices(anchorIS,&anchors));
9507           SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Point %" PetscInt_FMT " cannot be constrained and an anchor",p);
9508         }
9509       }
9510     }
9511     PetscCall(ISRestoreIndices(anchorIS,&anchors));
9512   }
9513   /* reset the generic constraints */
9514   PetscCall(DMSetDefaultConstraints(dm,NULL,NULL,NULL));
9515   PetscFunctionReturn(0);
9516 }
9517 
9518 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
9519 {
9520   PetscSection anchorSection;
9521   PetscInt pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
9522 
9523   PetscFunctionBegin;
9524   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9525   PetscCall(DMPlexGetAnchors(dm,&anchorSection,NULL));
9526   PetscCall(PetscSectionCreate(PETSC_COMM_SELF,cSec));
9527   PetscCall(PetscSectionGetNumFields(section,&numFields));
9528   if (numFields) {
9529     PetscInt f;
9530     PetscCall(PetscSectionSetNumFields(*cSec,numFields));
9531 
9532     for (f = 0; f < numFields; f++) {
9533       PetscInt numComp;
9534 
9535       PetscCall(PetscSectionGetFieldComponents(section,f,&numComp));
9536       PetscCall(PetscSectionSetFieldComponents(*cSec,f,numComp));
9537     }
9538   }
9539   PetscCall(PetscSectionGetChart(anchorSection,&pStart,&pEnd));
9540   PetscCall(PetscSectionGetChart(section,&sStart,&sEnd));
9541   pStart = PetscMax(pStart,sStart);
9542   pEnd   = PetscMin(pEnd,sEnd);
9543   pEnd   = PetscMax(pStart,pEnd);
9544   PetscCall(PetscSectionSetChart(*cSec,pStart,pEnd));
9545   for (p = pStart; p < pEnd; p++) {
9546     PetscCall(PetscSectionGetDof(anchorSection,p,&dof));
9547     if (dof) {
9548       PetscCall(PetscSectionGetDof(section,p,&dof));
9549       PetscCall(PetscSectionSetDof(*cSec,p,dof));
9550       for (f = 0; f < numFields; f++) {
9551         PetscCall(PetscSectionGetFieldDof(section,p,f,&dof));
9552         PetscCall(PetscSectionSetFieldDof(*cSec,p,f,dof));
9553       }
9554     }
9555   }
9556   PetscCall(PetscSectionSetUp(*cSec));
9557   PetscCall(PetscObjectSetName((PetscObject) *cSec, "Constraint Section"));
9558   PetscFunctionReturn(0);
9559 }
9560 
9561 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
9562 {
9563   PetscSection   aSec;
9564   PetscInt       pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
9565   const PetscInt *anchors;
9566   PetscInt       numFields, f;
9567   IS             aIS;
9568   MatType        mtype;
9569   PetscBool      iscuda,iskokkos;
9570 
9571   PetscFunctionBegin;
9572   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9573   PetscCall(PetscSectionGetStorageSize(cSec, &m));
9574   PetscCall(PetscSectionGetStorageSize(section, &n));
9575   PetscCall(MatCreate(PETSC_COMM_SELF,cMat));
9576   PetscCall(MatSetSizes(*cMat,m,n,m,n));
9577   PetscCall(PetscStrcmp(dm->mattype,MATSEQAIJCUSPARSE,&iscuda));
9578   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype,MATMPIAIJCUSPARSE,&iscuda));
9579   PetscCall(PetscStrcmp(dm->mattype,MATSEQAIJKOKKOS,&iskokkos));
9580   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype,MATMPIAIJKOKKOS,&iskokkos));
9581   if (iscuda) mtype = MATSEQAIJCUSPARSE;
9582   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
9583   else mtype = MATSEQAIJ;
9584   PetscCall(MatSetType(*cMat,mtype));
9585   PetscCall(DMPlexGetAnchors(dm,&aSec,&aIS));
9586   PetscCall(ISGetIndices(aIS,&anchors));
9587   /* cSec will be a subset of aSec and section */
9588   PetscCall(PetscSectionGetChart(cSec,&pStart,&pEnd));
9589   PetscCall(PetscSectionGetChart(section,&sStart,&sEnd));
9590   PetscCall(PetscMalloc1(m+1,&i));
9591   i[0] = 0;
9592   PetscCall(PetscSectionGetNumFields(section,&numFields));
9593   for (p = pStart; p < pEnd; p++) {
9594     PetscInt rDof, rOff, r;
9595 
9596     PetscCall(PetscSectionGetDof(aSec,p,&rDof));
9597     if (!rDof) continue;
9598     PetscCall(PetscSectionGetOffset(aSec,p,&rOff));
9599     if (numFields) {
9600       for (f = 0; f < numFields; f++) {
9601         annz = 0;
9602         for (r = 0; r < rDof; r++) {
9603           a = anchors[rOff + r];
9604           if (a < sStart || a >= sEnd) continue;
9605           PetscCall(PetscSectionGetFieldDof(section,a,f,&aDof));
9606           annz += aDof;
9607         }
9608         PetscCall(PetscSectionGetFieldDof(cSec,p,f,&dof));
9609         PetscCall(PetscSectionGetFieldOffset(cSec,p,f,&off));
9610         for (q = 0; q < dof; q++) {
9611           i[off + q + 1] = i[off + q] + annz;
9612         }
9613       }
9614     } else {
9615       annz = 0;
9616       PetscCall(PetscSectionGetDof(cSec,p,&dof));
9617       for (q = 0; q < dof; q++) {
9618         a = anchors[rOff + q];
9619         if (a < sStart || a >= sEnd) continue;
9620         PetscCall(PetscSectionGetDof(section,a,&aDof));
9621         annz += aDof;
9622       }
9623       PetscCall(PetscSectionGetDof(cSec,p,&dof));
9624       PetscCall(PetscSectionGetOffset(cSec,p,&off));
9625       for (q = 0; q < dof; q++) {
9626         i[off + q + 1] = i[off + q] + annz;
9627       }
9628     }
9629   }
9630   nnz = i[m];
9631   PetscCall(PetscMalloc1(nnz,&j));
9632   offset = 0;
9633   for (p = pStart; p < pEnd; p++) {
9634     if (numFields) {
9635       for (f = 0; f < numFields; f++) {
9636         PetscCall(PetscSectionGetFieldDof(cSec,p,f,&dof));
9637         for (q = 0; q < dof; q++) {
9638           PetscInt rDof, rOff, r;
9639           PetscCall(PetscSectionGetDof(aSec,p,&rDof));
9640           PetscCall(PetscSectionGetOffset(aSec,p,&rOff));
9641           for (r = 0; r < rDof; r++) {
9642             PetscInt s;
9643 
9644             a = anchors[rOff + r];
9645             if (a < sStart || a >= sEnd) continue;
9646             PetscCall(PetscSectionGetFieldDof(section,a,f,&aDof));
9647             PetscCall(PetscSectionGetFieldOffset(section,a,f,&aOff));
9648             for (s = 0; s < aDof; s++) {
9649               j[offset++] = aOff + s;
9650             }
9651           }
9652         }
9653       }
9654     } else {
9655       PetscCall(PetscSectionGetDof(cSec,p,&dof));
9656       for (q = 0; q < dof; q++) {
9657         PetscInt rDof, rOff, r;
9658         PetscCall(PetscSectionGetDof(aSec,p,&rDof));
9659         PetscCall(PetscSectionGetOffset(aSec,p,&rOff));
9660         for (r = 0; r < rDof; r++) {
9661           PetscInt s;
9662 
9663           a = anchors[rOff + r];
9664           if (a < sStart || a >= sEnd) continue;
9665           PetscCall(PetscSectionGetDof(section,a,&aDof));
9666           PetscCall(PetscSectionGetOffset(section,a,&aOff));
9667           for (s = 0; s < aDof; s++) {
9668             j[offset++] = aOff + s;
9669           }
9670         }
9671       }
9672     }
9673   }
9674   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat,i,j,NULL));
9675   PetscCall(PetscFree(i));
9676   PetscCall(PetscFree(j));
9677   PetscCall(ISRestoreIndices(aIS,&anchors));
9678   PetscFunctionReturn(0);
9679 }
9680 
9681 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
9682 {
9683   DM_Plex        *plex = (DM_Plex *)dm->data;
9684   PetscSection   anchorSection, section, cSec;
9685   Mat            cMat;
9686 
9687   PetscFunctionBegin;
9688   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9689   PetscCall(DMPlexGetAnchors(dm,&anchorSection,NULL));
9690   if (anchorSection) {
9691     PetscInt Nf;
9692 
9693     PetscCall(DMGetLocalSection(dm,&section));
9694     PetscCall(DMPlexCreateConstraintSection_Anchors(dm,section,&cSec));
9695     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm,section,cSec,&cMat));
9696     PetscCall(DMGetNumFields(dm,&Nf));
9697     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm,section,cSec,cMat));
9698     PetscCall(DMSetDefaultConstraints(dm,cSec,cMat,NULL));
9699     PetscCall(PetscSectionDestroy(&cSec));
9700     PetscCall(MatDestroy(&cMat));
9701   }
9702   PetscFunctionReturn(0);
9703 }
9704 
9705 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
9706 {
9707   IS             subis;
9708   PetscSection   section, subsection;
9709 
9710   PetscFunctionBegin;
9711   PetscCall(DMGetLocalSection(dm, &section));
9712   PetscCheck(section,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
9713   PetscCheck(subdm,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
9714   /* Create subdomain */
9715   PetscCall(DMPlexFilter(dm, label, value, subdm));
9716   /* Create submodel */
9717   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
9718   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
9719   PetscCall(DMSetLocalSection(*subdm, subsection));
9720   PetscCall(PetscSectionDestroy(&subsection));
9721   PetscCall(DMCopyDisc(dm, *subdm));
9722   /* Create map from submodel to global model */
9723   if (is) {
9724     PetscSection    sectionGlobal, subsectionGlobal;
9725     IS              spIS;
9726     const PetscInt *spmap;
9727     PetscInt       *subIndices;
9728     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
9729     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
9730 
9731     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
9732     PetscCall(ISGetIndices(spIS, &spmap));
9733     PetscCall(PetscSectionGetNumFields(section, &Nf));
9734     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
9735     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
9736     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
9737     for (p = pStart; p < pEnd; ++p) {
9738       PetscInt gdof, pSubSize  = 0;
9739 
9740       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
9741       if (gdof > 0) {
9742         for (f = 0; f < Nf; ++f) {
9743           PetscInt fdof, fcdof;
9744 
9745           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
9746           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
9747           pSubSize += fdof-fcdof;
9748         }
9749         subSize += pSubSize;
9750         if (pSubSize) {
9751           if (bs < 0) {
9752             bs = pSubSize;
9753           } else if (bs != pSubSize) {
9754             /* Layout does not admit a pointwise block size */
9755             bs = 1;
9756           }
9757         }
9758       }
9759     }
9760     /* Must have same blocksize on all procs (some might have no points) */
9761     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs; bsLocal[1] = bs;
9762     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject) dm), bsLocal, bsMinMax));
9763     if (bsMinMax[0] != bsMinMax[1]) {bs = 1;}
9764     else                            {bs = bsMinMax[0];}
9765     PetscCall(PetscMalloc1(subSize, &subIndices));
9766     for (p = pStart; p < pEnd; ++p) {
9767       PetscInt gdof, goff;
9768 
9769       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
9770       if (gdof > 0) {
9771         const PetscInt point = spmap[p];
9772 
9773         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
9774         for (f = 0; f < Nf; ++f) {
9775           PetscInt fdof, fcdof, fc, f2, poff = 0;
9776 
9777           /* Can get rid of this loop by storing field information in the global section */
9778           for (f2 = 0; f2 < f; ++f2) {
9779             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
9780             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
9781             poff += fdof-fcdof;
9782           }
9783           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
9784           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
9785           for (fc = 0; fc < fdof-fcdof; ++fc, ++subOff) {
9786             subIndices[subOff] = goff+poff+fc;
9787           }
9788         }
9789       }
9790     }
9791     PetscCall(ISRestoreIndices(spIS, &spmap));
9792     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
9793     if (bs > 1) {
9794       /* We need to check that the block size does not come from non-contiguous fields */
9795       PetscInt i, j, set = 1;
9796       for (i = 0; i < subSize; i += bs) {
9797         for (j = 0; j < bs; ++j) {
9798           if (subIndices[i+j] != subIndices[i]+j) {set = 0; break;}
9799         }
9800       }
9801       if (set) PetscCall(ISSetBlockSize(*is, bs));
9802     }
9803     /* Attach nullspace */
9804     for (f = 0; f < Nf; ++f) {
9805       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
9806       if ((*subdm)->nullspaceConstructors[f]) break;
9807     }
9808     if (f < Nf) {
9809       MatNullSpace nullSpace;
9810       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
9811 
9812       PetscCall(PetscObjectCompose((PetscObject) *is, "nullspace", (PetscObject) nullSpace));
9813       PetscCall(MatNullSpaceDestroy(&nullSpace));
9814     }
9815   }
9816   PetscFunctionReturn(0);
9817 }
9818 
9819 /*@
9820   DMPlexMonitorThroughput - Report the cell throughput of FE integration
9821 
9822   Input Parameter:
9823 - dm - The DM
9824 
9825   Level: developer
9826 
9827   Options Database Keys:
9828 . -dm_plex_monitor_throughput - Activate the monitor
9829 
9830 .seealso: `DMSetFromOptions()`, `DMPlexCreate()`
9831 @*/
9832 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
9833 {
9834 #if defined(PETSC_USE_LOG)
9835   PetscStageLog      stageLog;
9836   PetscLogEvent      event;
9837   PetscLogStage      stage;
9838   PetscEventPerfInfo eventInfo;
9839   PetscReal          cellRate, flopRate;
9840   PetscInt           cStart, cEnd, Nf, N;
9841   const char        *name;
9842 #endif
9843 
9844   PetscFunctionBegin;
9845   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9846 #if defined(PETSC_USE_LOG)
9847   PetscCall(PetscObjectGetName((PetscObject) dm, &name));
9848   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9849   PetscCall(DMGetNumFields(dm, &Nf));
9850   PetscCall(PetscLogGetStageLog(&stageLog));
9851   PetscCall(PetscStageLogGetCurrent(stageLog, &stage));
9852   PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
9853   PetscCall(PetscLogEventGetPerfInfo(stage, event, &eventInfo));
9854   N        = (cEnd - cStart)*Nf*eventInfo.count;
9855   flopRate = eventInfo.flops/eventInfo.time;
9856   cellRate = N/eventInfo.time;
9857   PetscCall(PetscPrintf(PetscObjectComm((PetscObject) dm), "DM (%s) FE Residual Integration: %" PetscInt_FMT " integrals %d reps\n  Cell rate: %.2g/s flop rate: %.2g MF/s\n", name ? name : "unknown", N, eventInfo.count, (double) cellRate, (double) (flopRate/1.e6)));
9858 #else
9859   SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Plex Throughput Monitor is not supported if logging is turned off. Reconfigure using --with-log.");
9860 #endif
9861   PetscFunctionReturn(0);
9862 }
9863