xref: /petsc/src/dm/impls/plex/plex.c (revision 26800d79828faf09ca11b43c397e6c2732195674)
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 {
622     PetscCall(VecLoad_Default(v, viewer));
623   }
624   PetscFunctionReturn(0);
625 }
626 
627 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
628 {
629   DM             dm;
630   PetscBool      ishdf5,isexodusii;
631 
632   PetscFunctionBegin;
633   PetscCall(VecGetDM(v, &dm));
634   PetscCheck(dm,PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
635   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,     &ishdf5));
636   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWEREXODUSII, &isexodusii));
637   if (ishdf5) {
638 #if defined(PETSC_HAVE_HDF5)
639     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
640 #else
641     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
642 #endif
643   } else if (isexodusii) {
644 #if defined(PETSC_HAVE_EXODUSII)
645     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
646 #else
647     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
648 #endif
649   } else {
650     PetscCall(VecLoad_Default(v, viewer));
651   }
652   PetscFunctionReturn(0);
653 }
654 
655 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
656 {
657   DM                dm;
658   PetscViewerFormat format;
659   PetscBool         ishdf5;
660 
661   PetscFunctionBegin;
662   PetscCall(VecGetDM(originalv, &dm));
663   PetscCheck(dm,PetscObjectComm((PetscObject) originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
664   PetscCall(PetscViewerGetFormat(viewer, &format));
665   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
666   if (format == PETSC_VIEWER_NATIVE) {
667     if (dm->useNatural) {
668       if (dm->sfNatural) {
669         if (ishdf5) {
670 #if defined(PETSC_HAVE_HDF5)
671           Vec         v;
672           const char *vecname;
673 
674           PetscCall(DMGetGlobalVector(dm, &v));
675           PetscCall(PetscObjectGetName((PetscObject) originalv, &vecname));
676           PetscCall(PetscObjectSetName((PetscObject) v, vecname));
677           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
678           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
679           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
680           PetscCall(DMRestoreGlobalVector(dm, &v));
681 #else
682           SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
683 #endif
684         } else SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
685       }
686     } else {
687       PetscCall(VecLoad_Default(originalv, viewer));
688     }
689   }
690   PetscFunctionReturn(0);
691 }
692 
693 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
694 {
695   PetscSection       coordSection;
696   Vec                coordinates;
697   DMLabel            depthLabel, celltypeLabel;
698   const char        *name[4];
699   const PetscScalar *a;
700   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
701 
702   PetscFunctionBegin;
703   PetscCall(DMGetDimension(dm, &dim));
704   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
705   PetscCall(DMGetCoordinateSection(dm, &coordSection));
706   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
707   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
708   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
709   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
710   PetscCall(VecGetArrayRead(coordinates, &a));
711   name[0]     = "vertex";
712   name[1]     = "edge";
713   name[dim-1] = "face";
714   name[dim]   = "cell";
715   for (c = cStart; c < cEnd; ++c) {
716     PetscInt *closure = NULL;
717     PetscInt  closureSize, cl, ct;
718 
719     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
720     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
721     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
722     PetscCall(PetscViewerASCIIPushTab(viewer));
723     for (cl = 0; cl < closureSize*2; cl += 2) {
724       PetscInt point = closure[cl], depth, dof, off, d, p;
725 
726       if ((point < pStart) || (point >= pEnd)) continue;
727       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
728       if (!dof) continue;
729       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
730       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
731       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
732       for (p = 0; p < dof/dim; ++p) {
733         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
734         for (d = 0; d < dim; ++d) {
735           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
736           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double) PetscRealPart(a[off+p*dim+d])));
737         }
738         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
739       }
740       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
741     }
742     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
743     PetscCall(PetscViewerASCIIPopTab(viewer));
744   }
745   PetscCall(VecRestoreArrayRead(coordinates, &a));
746   PetscFunctionReturn(0);
747 }
748 
749 typedef enum {CS_CARTESIAN, CS_POLAR, CS_CYLINDRICAL, CS_SPHERICAL} CoordSystem;
750 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
751 
752 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
753 {
754   PetscInt       i;
755 
756   PetscFunctionBegin;
757   if (dim > 3) {
758     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double) PetscRealPart(x[i])));
759   } else {
760     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
761 
762     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
763     switch (cs) {
764       case CS_CARTESIAN: for (i = 0; i < dim; ++i) trcoords[i] = coords[i];break;
765       case CS_POLAR:
766         PetscCheck(dim == 2,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
767         trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
768         trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
769         break;
770       case CS_CYLINDRICAL:
771         PetscCheck(dim == 3,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
772         trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
773         trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
774         trcoords[2] = coords[2];
775         break;
776       case CS_SPHERICAL:
777         PetscCheck(dim == 3,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
778         trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
779         trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
780         trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
781         break;
782     }
783     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double) trcoords[i]));
784   }
785   PetscFunctionReturn(0);
786 }
787 
788 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
789 {
790   DM_Plex          *mesh = (DM_Plex*) dm->data;
791   DM                cdm;
792   PetscSection      coordSection;
793   Vec               coordinates;
794   PetscViewerFormat format;
795 
796   PetscFunctionBegin;
797   PetscCall(DMGetCoordinateDM(dm, &cdm));
798   PetscCall(DMGetLocalSection(cdm, &coordSection));
799   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
800   PetscCall(PetscViewerGetFormat(viewer, &format));
801   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
802     const char *name;
803     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
804     PetscInt    pStart, pEnd, p, numLabels, l;
805     PetscMPIInt rank, size;
806 
807     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
808     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
809     PetscCall(PetscObjectGetName((PetscObject) dm, &name));
810     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
811     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
812     PetscCall(DMGetDimension(dm, &dim));
813     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
814     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
815     else      PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
816     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
817     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
818     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
819     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
820     for (p = pStart; p < pEnd; ++p) {
821       PetscInt dof, off, s;
822 
823       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
824       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
825       for (s = off; s < off+dof; ++s) {
826         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
827       }
828     }
829     PetscCall(PetscViewerFlush(viewer));
830     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
831     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
832     for (p = pStart; p < pEnd; ++p) {
833       PetscInt dof, off, c;
834 
835       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
836       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
837       for (c = off; c < off+dof; ++c) {
838         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " <---- %" PetscInt_FMT " (%" PetscInt_FMT ")\n", rank, p, mesh->cones[c], mesh->coneOrientations[c]));
839       }
840     }
841     PetscCall(PetscViewerFlush(viewer));
842     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
843     if (coordSection && coordinates) {
844       CoordSystem        cs = CS_CARTESIAN;
845       const PetscScalar *array;
846       PetscInt           Nf, Nc, pStart, pEnd, p;
847       PetscMPIInt        rank;
848       const char        *name;
849 
850       PetscCall(PetscOptionsGetEnum(((PetscObject) viewer)->options, ((PetscObject) viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *) &cs, NULL));
851       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
852       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
853       PetscCheck(Nf == 1,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
854       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
855       PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
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       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
863       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
864       for (p = pStart; p < pEnd; ++p) {
865         PetscInt dof, off;
866 
867         PetscCall(PetscSectionGetDof(coordSection, p, &dof));
868         PetscCall(PetscSectionGetOffset(coordSection, p, &off));
869         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
870         PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
871         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
872       }
873       PetscCall(PetscViewerFlush(viewer));
874       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
875       PetscCall(VecRestoreArrayRead(coordinates, &array));
876     }
877     PetscCall(DMGetNumLabels(dm, &numLabels));
878     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
879     for (l = 0; l < numLabels; ++l) {
880       DMLabel     label;
881       PetscBool   isdepth;
882       const char *name;
883 
884       PetscCall(DMGetLabelName(dm, l, &name));
885       PetscCall(PetscStrcmp(name, "depth", &isdepth));
886       if (isdepth) continue;
887       PetscCall(DMGetLabel(dm, name, &label));
888       PetscCall(DMLabelView(label, viewer));
889     }
890     if (size > 1) {
891       PetscSF sf;
892 
893       PetscCall(DMGetPointSF(dm, &sf));
894       PetscCall(PetscSFView(sf, viewer));
895     }
896     PetscCall(PetscViewerFlush(viewer));
897   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
898     const char  *name, *color;
899     const char  *defcolors[3]  = {"gray", "orange", "green"};
900     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
901     char         lname[PETSC_MAX_PATH_LEN];
902     PetscReal    scale         = 2.0;
903     PetscReal    tikzscale     = 1.0;
904     PetscBool    useNumbers    = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
905     double       tcoords[3];
906     PetscScalar *coords;
907     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
908     PetscMPIInt  rank, size;
909     char         **names, **colors, **lcolors;
910     PetscBool    flg, lflg;
911     PetscBT      wp = NULL;
912     PetscInt     pEnd, pStart;
913 
914     PetscCall(DMGetDimension(dm, &dim));
915     PetscCall(DMPlexGetDepth(dm, &depth));
916     PetscCall(DMGetNumLabels(dm, &numLabels));
917     numLabels  = PetscMax(numLabels, 10);
918     numColors  = 10;
919     numLColors = 10;
920     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
921     PetscCall(PetscOptionsGetReal(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
922     PetscCall(PetscOptionsGetReal(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
923     PetscCall(PetscOptionsGetBool(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
924     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
925     for (d = 0; d < 4; ++d) drawColors[d]  = PETSC_TRUE;
926     n = 4;
927     PetscCall(PetscOptionsGetBoolArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
928     PetscCheck(!flg || n == dim+1,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim+1);
929     PetscCall(PetscOptionsGetBoolArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
930     PetscCheck(!flg || n == dim+1,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim+1);
931     PetscCall(PetscOptionsGetStringArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
932     if (!useLabels) numLabels = 0;
933     PetscCall(PetscOptionsGetStringArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
934     if (!useColors) {
935       numColors = 3;
936       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
937     }
938     PetscCall(PetscOptionsGetStringArray(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
939     if (!useColors) {
940       numLColors = 4;
941       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
942     }
943     PetscCall(PetscOptionsGetString(((PetscObject) viewer)->options, ((PetscObject) viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
944     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
945     PetscCall(PetscOptionsGetBool(((PetscObject) viewer)->options,((PetscObject) viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
946     PetscCheck(!flg || !plotEdges || depth >= dim,PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Mesh must be interpolated");
947     if (depth < dim) plotEdges = PETSC_FALSE;
948     PetscCall(PetscOptionsGetBool(((PetscObject) viewer)->options, ((PetscObject) viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
949 
950     /* filter points with labelvalue != labeldefaultvalue */
951     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
952     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
953     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
954     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
955     if (lflg) {
956       DMLabel lbl;
957 
958       PetscCall(DMGetLabel(dm, lname, &lbl));
959       if (lbl) {
960         PetscInt val, defval;
961 
962         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
963         PetscCall(PetscBTCreate(pEnd-pStart, &wp));
964         for (c = pStart;  c < pEnd; c++) {
965           PetscInt *closure = NULL;
966           PetscInt  closureSize;
967 
968           PetscCall(DMLabelGetValue(lbl, c, &val));
969           if (val == defval) continue;
970 
971           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
972           for (p = 0; p < closureSize*2; p += 2) {
973             PetscCall(PetscBTSet(wp, closure[p] - pStart));
974           }
975           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
976         }
977       }
978     }
979 
980     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
981     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
982     PetscCall(PetscObjectGetName((PetscObject) dm, &name));
983     PetscCall(PetscViewerASCIIPrintf(viewer, "\
984 \\documentclass[tikz]{standalone}\n\n\
985 \\usepackage{pgflibraryshapes}\n\
986 \\usetikzlibrary{backgrounds}\n\
987 \\usetikzlibrary{arrows}\n\
988 \\begin{document}\n"));
989     if (size > 1) {
990       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
991       for (p = 0; p < size; ++p) {
992         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size-1) ? ", and " :  ", "));
993         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p%numColors], p));
994       }
995       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
996     }
997     if (drawHasse) {
998       PetscInt maxStratum = PetscMax(vEnd-vStart, PetscMax(eEnd-eStart, cEnd-cStart));
999 
1000       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1001       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd-1));
1002       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd-vStart));
1003       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum-(vEnd-vStart))/2.));
1004       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1005       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd-1));
1006       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum-(eEnd-eStart))/2.));
1007       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd-eStart));
1008       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1009       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd-1));
1010       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd-cStart));
1011       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum-(cEnd-cStart))/2.));
1012     }
1013     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double) tikzscale));
1014 
1015     /* Plot vertices */
1016     PetscCall(VecGetArray(coordinates, &coords));
1017     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1018     for (v = vStart; v < vEnd; ++v) {
1019       PetscInt  off, dof, d;
1020       PetscBool isLabeled = PETSC_FALSE;
1021 
1022       if (wp && !PetscBTLookup(wp,v - pStart)) continue;
1023       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1024       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1025       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1026       PetscCheck(dof <= 3,PETSC_COMM_SELF,PETSC_ERR_PLIB,"coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3",v,dof);
1027       for (d = 0; d < dof; ++d) {
1028         tcoords[d] = (double) (scale*PetscRealPart(coords[off+d]));
1029         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1030       }
1031       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1032       if (dim == 3) {PetscReal tmp = tcoords[1]; tcoords[1] = tcoords[2]; tcoords[2] = -tmp;}
1033       for (d = 0; d < dof; ++d) {
1034         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1035         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double) tcoords[d]));
1036       }
1037       if (drawHasse) color = colors[0%numColors];
1038       else           color = colors[rank%numColors];
1039       for (l = 0; l < numLabels; ++l) {
1040         PetscInt val;
1041         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1042         if (val >= 0) {color = lcolors[l%numLColors]; isLabeled = PETSC_TRUE; break;}
1043       }
1044       if (drawNumbers[0]) {
1045         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1046       } else if (drawColors[0]) {
1047         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1048       } else {
1049         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1050       }
1051     }
1052     PetscCall(VecRestoreArray(coordinates, &coords));
1053     PetscCall(PetscViewerFlush(viewer));
1054     /* Plot edges */
1055     if (plotEdges) {
1056       PetscCall(VecGetArray(coordinates, &coords));
1057       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1058       for (e = eStart; e < eEnd; ++e) {
1059         const PetscInt *cone;
1060         PetscInt        coneSize, offA, offB, dof, d;
1061 
1062         if (wp && !PetscBTLookup(wp,e - pStart)) continue;
1063         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1064         PetscCheck(coneSize == 2,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1065         PetscCall(DMPlexGetCone(dm, e, &cone));
1066         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1067         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1068         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1069         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1070         for (d = 0; d < dof; ++d) {
1071           tcoords[d] = (double) (0.5*scale*PetscRealPart(coords[offA+d]+coords[offB+d]));
1072           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1073         }
1074         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1075         if (dim == 3) {PetscReal tmp = tcoords[1]; tcoords[1] = tcoords[2]; tcoords[2] = -tmp;}
1076         for (d = 0; d < dof; ++d) {
1077           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1078           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1079         }
1080         if (drawHasse) color = colors[1%numColors];
1081         else           color = colors[rank%numColors];
1082         for (l = 0; l < numLabels; ++l) {
1083           PetscInt val;
1084           PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1085           if (val >= 0) {color = lcolors[l%numLColors]; break;}
1086         }
1087         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1088       }
1089       PetscCall(VecRestoreArray(coordinates, &coords));
1090       PetscCall(PetscViewerFlush(viewer));
1091       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1092     }
1093     /* Plot cells */
1094     if (dim == 3 || !drawNumbers[1]) {
1095       for (e = eStart; e < eEnd; ++e) {
1096         const PetscInt *cone;
1097 
1098         if (wp && !PetscBTLookup(wp,e - pStart)) continue;
1099         color = colors[rank%numColors];
1100         for (l = 0; l < numLabels; ++l) {
1101           PetscInt val;
1102           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1103           if (val >= 0) {color = lcolors[l%numLColors]; break;}
1104         }
1105         PetscCall(DMPlexGetCone(dm, e, &cone));
1106         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1107       }
1108     } else {
1109        DMPolytopeType ct;
1110 
1111       /* Drawing a 2D polygon */
1112       for (c = cStart; c < cEnd; ++c) {
1113         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1114         PetscCall(DMPlexGetCellType(dm, c, &ct));
1115         if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR ||
1116             ct == DM_POLYTOPE_TRI_PRISM_TENSOR ||
1117             ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
1118           const PetscInt *cone;
1119           PetscInt        coneSize, e;
1120 
1121           PetscCall(DMPlexGetCone(dm, c, &cone));
1122           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1123           for (e = 0; e < coneSize; ++e) {
1124             const PetscInt *econe;
1125 
1126             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1127             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));
1128           }
1129         } else {
1130           PetscInt *closure = NULL;
1131           PetscInt  closureSize, Nv = 0, v;
1132 
1133           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1134           for (p = 0; p < closureSize*2; p += 2) {
1135             const PetscInt point = closure[p];
1136 
1137             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1138           }
1139           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank%numColors]));
1140           for (v = 0; v <= Nv; ++v) {
1141             const PetscInt vertex = closure[v%Nv];
1142 
1143             if (v > 0) {
1144               if (plotEdges) {
1145                 const PetscInt *edge;
1146                 PetscInt        endpoints[2], ne;
1147 
1148                 endpoints[0] = closure[v-1]; endpoints[1] = vertex;
1149                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1150                 PetscCheck(ne == 1,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1151                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1152                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1153               } else {
1154                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1155               }
1156             }
1157             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1158           }
1159           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1160           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1161         }
1162       }
1163     }
1164     PetscCall(VecGetArray(coordinates, &coords));
1165     for (c = cStart; c < cEnd; ++c) {
1166       double    ccoords[3] = {0.0, 0.0, 0.0};
1167       PetscBool isLabeled  = PETSC_FALSE;
1168       PetscInt *closure    = NULL;
1169       PetscInt  closureSize, dof, d, n = 0;
1170 
1171       if (wp && !PetscBTLookup(wp,c - pStart)) continue;
1172       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1173       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1174       for (p = 0; p < closureSize*2; p += 2) {
1175         const PetscInt point = closure[p];
1176         PetscInt       off;
1177 
1178         if ((point < vStart) || (point >= vEnd)) continue;
1179         PetscCall(PetscSectionGetDof(coordSection, point, &dof));
1180         PetscCall(PetscSectionGetOffset(coordSection, point, &off));
1181         for (d = 0; d < dof; ++d) {
1182           tcoords[d] = (double) (scale*PetscRealPart(coords[off+d]));
1183           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1184         }
1185         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1186         if (dof == 3) {PetscReal tmp = tcoords[1]; tcoords[1] = tcoords[2]; tcoords[2] = -tmp;}
1187         for (d = 0; d < dof; ++d) {ccoords[d] += tcoords[d];}
1188         ++n;
1189       }
1190       for (d = 0; d < dof; ++d) {ccoords[d] /= n;}
1191       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1192       for (d = 0; d < dof; ++d) {
1193         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1194         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double) ccoords[d]));
1195       }
1196       if (drawHasse) color = colors[depth%numColors];
1197       else           color = colors[rank%numColors];
1198       for (l = 0; l < numLabels; ++l) {
1199         PetscInt val;
1200         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1201         if (val >= 0) {color = lcolors[l%numLColors]; isLabeled = PETSC_TRUE; break;}
1202       }
1203       if (drawNumbers[dim]) {
1204         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1205       } else if (drawColors[dim]) {
1206         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1207       } else {
1208         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1209       }
1210     }
1211     PetscCall(VecRestoreArray(coordinates, &coords));
1212     if (drawHasse) {
1213       color = colors[depth%numColors];
1214       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1215       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1216       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1217       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1218       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1219 
1220       color = colors[1%numColors];
1221       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1222       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1223       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1224       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1225       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1226 
1227       color = colors[0%numColors];
1228       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1229       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1230       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1231       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1232       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1233 
1234       for (p = pStart; p < pEnd; ++p) {
1235         const PetscInt *cone;
1236         PetscInt        coneSize, cp;
1237 
1238         PetscCall(DMPlexGetCone(dm, p, &cone));
1239         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1240         for (cp = 0; cp < coneSize; ++cp) {
1241           PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1242         }
1243       }
1244     }
1245     PetscCall(PetscViewerFlush(viewer));
1246     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1247     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1248     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1249     for (l = 0; l < numLabels;  ++l) PetscCall(PetscFree(names[l]));
1250     for (c = 0; c < numColors;  ++c) PetscCall(PetscFree(colors[c]));
1251     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1252     PetscCall(PetscFree3(names, colors, lcolors));
1253     PetscCall(PetscBTDestroy(&wp));
1254   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1255     Vec                    cown,acown;
1256     VecScatter             sct;
1257     ISLocalToGlobalMapping g2l;
1258     IS                     gid,acis;
1259     MPI_Comm               comm,ncomm = MPI_COMM_NULL;
1260     MPI_Group              ggroup,ngroup;
1261     PetscScalar            *array,nid;
1262     const PetscInt         *idxs;
1263     PetscInt               *idxs2,*start,*adjacency,*work;
1264     PetscInt64             lm[3],gm[3];
1265     PetscInt               i,c,cStart,cEnd,cum,numVertices,ect,ectn,cellHeight;
1266     PetscMPIInt            d1,d2,rank;
1267 
1268     PetscCall(PetscObjectGetComm((PetscObject)dm,&comm));
1269     PetscCallMPI(MPI_Comm_rank(comm,&rank));
1270 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1271     PetscCallMPI(MPI_Comm_split_type(comm,MPI_COMM_TYPE_SHARED,rank,MPI_INFO_NULL,&ncomm));
1272 #endif
1273     if (ncomm != MPI_COMM_NULL) {
1274       PetscCallMPI(MPI_Comm_group(comm,&ggroup));
1275       PetscCallMPI(MPI_Comm_group(ncomm,&ngroup));
1276       d1   = 0;
1277       PetscCallMPI(MPI_Group_translate_ranks(ngroup,1,&d1,ggroup,&d2));
1278       nid  = d2;
1279       PetscCallMPI(MPI_Group_free(&ggroup));
1280       PetscCallMPI(MPI_Group_free(&ngroup));
1281       PetscCallMPI(MPI_Comm_free(&ncomm));
1282     } else nid = 0.0;
1283 
1284     /* Get connectivity */
1285     PetscCall(DMPlexGetVTKCellHeight(dm,&cellHeight));
1286     PetscCall(DMPlexCreatePartitionerGraph(dm,cellHeight,&numVertices,&start,&adjacency,&gid));
1287 
1288     /* filter overlapped local cells */
1289     PetscCall(DMPlexGetHeightStratum(dm,cellHeight,&cStart,&cEnd));
1290     PetscCall(ISGetIndices(gid,&idxs));
1291     PetscCall(ISGetLocalSize(gid,&cum));
1292     PetscCall(PetscMalloc1(cum,&idxs2));
1293     for (c = cStart, cum = 0; c < cEnd; c++) {
1294       if (idxs[c-cStart] < 0) continue;
1295       idxs2[cum++] = idxs[c-cStart];
1296     }
1297     PetscCall(ISRestoreIndices(gid,&idxs));
1298     PetscCheck(numVertices == cum,PETSC_COMM_SELF,PETSC_ERR_PLIB,"Unexpected %" PetscInt_FMT " != %" PetscInt_FMT,numVertices,cum);
1299     PetscCall(ISDestroy(&gid));
1300     PetscCall(ISCreateGeneral(comm,numVertices,idxs2,PETSC_OWN_POINTER,&gid));
1301 
1302     /* support for node-aware cell locality */
1303     PetscCall(ISCreateGeneral(comm,start[numVertices],adjacency,PETSC_USE_POINTER,&acis));
1304     PetscCall(VecCreateSeq(PETSC_COMM_SELF,start[numVertices],&acown));
1305     PetscCall(VecCreateMPI(comm,numVertices,PETSC_DECIDE,&cown));
1306     PetscCall(VecGetArray(cown,&array));
1307     for (c = 0; c < numVertices; c++) array[c] = nid;
1308     PetscCall(VecRestoreArray(cown,&array));
1309     PetscCall(VecScatterCreate(cown,acis,acown,NULL,&sct));
1310     PetscCall(VecScatterBegin(sct,cown,acown,INSERT_VALUES,SCATTER_FORWARD));
1311     PetscCall(VecScatterEnd(sct,cown,acown,INSERT_VALUES,SCATTER_FORWARD));
1312     PetscCall(ISDestroy(&acis));
1313     PetscCall(VecScatterDestroy(&sct));
1314     PetscCall(VecDestroy(&cown));
1315 
1316     /* compute edgeCut */
1317     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum,start[c+1]-start[c]);
1318     PetscCall(PetscMalloc1(cum,&work));
1319     PetscCall(ISLocalToGlobalMappingCreateIS(gid,&g2l));
1320     PetscCall(ISLocalToGlobalMappingSetType(g2l,ISLOCALTOGLOBALMAPPINGHASH));
1321     PetscCall(ISDestroy(&gid));
1322     PetscCall(VecGetArray(acown,&array));
1323     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1324       PetscInt totl;
1325 
1326       totl = start[c+1]-start[c];
1327       PetscCall(ISGlobalToLocalMappingApply(g2l,IS_GTOLM_MASK,totl,adjacency+start[c],NULL,work));
1328       for (i = 0; i < totl; i++) {
1329         if (work[i] < 0) {
1330           ect  += 1;
1331           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1332         }
1333       }
1334     }
1335     PetscCall(PetscFree(work));
1336     PetscCall(VecRestoreArray(acown,&array));
1337     lm[0] = numVertices > 0 ?  numVertices : PETSC_MAX_INT;
1338     lm[1] = -numVertices;
1339     PetscCall(MPIU_Allreduce(lm,gm,2,MPIU_INT64,MPI_MIN,comm));
1340     PetscCall(PetscViewerASCIIPrintf(viewer,"  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT,-((double)gm[1])/((double)gm[0]),-(PetscInt)gm[1],(PetscInt)gm[0]));
1341     lm[0] = ect; /* edgeCut */
1342     lm[1] = ectn; /* node-aware edgeCut */
1343     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1344     PetscCall(MPIU_Allreduce(lm,gm,3,MPIU_INT64,MPI_SUM,comm));
1345     PetscCall(PetscViewerASCIIPrintf(viewer,", empty %" PetscInt_FMT ")\n",(PetscInt)gm[2]));
1346 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1347     PetscCall(PetscViewerASCIIPrintf(viewer,"  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n",(PetscInt)(gm[0]/2),gm[0] ? ((double)(gm[1]))/((double)gm[0]) : 1.));
1348 #else
1349     PetscCall(PetscViewerASCIIPrintf(viewer,"  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n",(PetscInt)(gm[0]/2),0.0));
1350 #endif
1351     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1352     PetscCall(PetscFree(start));
1353     PetscCall(PetscFree(adjacency));
1354     PetscCall(VecDestroy(&acown));
1355   } else {
1356     const char    *name;
1357     PetscInt      *sizes, *hybsizes, *ghostsizes;
1358     PetscInt       locDepth, depth, cellHeight, dim, d;
1359     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1360     PetscInt       numLabels, l, maxSize = 17;
1361     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1362     MPI_Comm       comm;
1363     PetscMPIInt    size, rank;
1364 
1365     PetscCall(PetscObjectGetComm((PetscObject) dm, &comm));
1366     PetscCallMPI(MPI_Comm_size(comm, &size));
1367     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1368     PetscCall(DMGetDimension(dm, &dim));
1369     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1370     PetscCall(PetscObjectGetName((PetscObject) dm, &name));
1371     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1372     else      PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1373     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1374     PetscCall(DMPlexGetDepth(dm, &locDepth));
1375     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1376     PetscCall(DMPlexGetGhostCellStratum(dm, &gcStart, &gcEnd));
1377     gcNum = gcEnd - gcStart;
1378     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1379     else                PetscCall(PetscCalloc3(3,    &sizes, 3,    &hybsizes, 3,    &ghostsizes));
1380     for (d = 0; d <= depth; d++) {
1381       PetscInt Nc[2] = {0, 0}, ict;
1382 
1383       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1384       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1385       ict  = ct0;
1386       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1387       ct0  = (DMPolytopeType) ict;
1388       for (p = pStart; p < pEnd; ++p) {
1389         DMPolytopeType ct;
1390 
1391         PetscCall(DMPlexGetCellType(dm, p, &ct));
1392         if (ct == ct0) ++Nc[0];
1393         else           ++Nc[1];
1394       }
1395       if (size < maxSize) {
1396         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes,    1, MPIU_INT, 0, comm));
1397         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1398         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1399         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1400         for (p = 0; p < size; ++p) {
1401           if (rank == 0) {
1402             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p]+hybsizes[p]));
1403             if (hybsizes[p]   > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1404             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1405           }
1406         }
1407       } else {
1408         PetscInt locMinMax[2];
1409 
1410         locMinMax[0] = Nc[0]+Nc[1]; locMinMax[1] = Nc[0]+Nc[1];
1411         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1412         locMinMax[0] = Nc[1]; locMinMax[1] = Nc[1];
1413         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1414         if (d == depth) {
1415           locMinMax[0] = gcNum; locMinMax[1] = gcNum;
1416           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1417         }
1418         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1419         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1420         if (hybsizes[0]   > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1421         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1422       }
1423       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1424     }
1425     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1426     {
1427       const PetscReal      *maxCell;
1428       const PetscReal      *L;
1429       const DMBoundaryType *bd;
1430       PetscBool             per, localized;
1431 
1432       PetscCall(DMGetPeriodicity(dm, &per, &maxCell, &L, &bd));
1433       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1434       if (per) {
1435         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh ("));
1436         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1437         for (d = 0; d < dim; ++d) {
1438           if (bd && d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1439           if (bd)    PetscCall(PetscViewerASCIIPrintf(viewer, "%s", DMBoundaryTypes[bd[d]]));
1440         }
1441         PetscCall(PetscViewerASCIIPrintf(viewer, ") coordinates %s\n", localized ? "localized" : "not localized"));
1442         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1443       }
1444     }
1445     PetscCall(DMGetNumLabels(dm, &numLabels));
1446     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1447     for (l = 0; l < numLabels; ++l) {
1448       DMLabel         label;
1449       const char     *name;
1450       IS              valueIS;
1451       const PetscInt *values;
1452       PetscInt        numValues, v;
1453 
1454       PetscCall(DMGetLabelName(dm, l, &name));
1455       PetscCall(DMGetLabel(dm, name, &label));
1456       PetscCall(DMLabelGetNumValues(label, &numValues));
1457       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1458       PetscCall(DMLabelGetValueIS(label, &valueIS));
1459       PetscCall(ISGetIndices(valueIS, &values));
1460       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1461       for (v = 0; v < numValues; ++v) {
1462         PetscInt size;
1463 
1464         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1465         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1466         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1467       }
1468       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1469       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1470       PetscCall(ISRestoreIndices(valueIS, &values));
1471       PetscCall(ISDestroy(&valueIS));
1472     }
1473     {
1474       char    **labelNames;
1475       PetscInt  Nl = numLabels;
1476       PetscBool flg;
1477 
1478       PetscCall(PetscMalloc1(Nl, &labelNames));
1479       PetscCall(PetscOptionsGetStringArray(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1480       for (l = 0; l < Nl; ++l) {
1481         DMLabel label;
1482 
1483         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1484         if (flg) {
1485           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1486           PetscCall(DMLabelView(label, viewer));
1487         }
1488         PetscCall(PetscFree(labelNames[l]));
1489       }
1490       PetscCall(PetscFree(labelNames));
1491     }
1492     /* If no fields are specified, people do not want to see adjacency */
1493     if (dm->Nf) {
1494       PetscInt f;
1495 
1496       for (f = 0; f < dm->Nf; ++f) {
1497         const char *name;
1498 
1499         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1500         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1501         PetscCall(PetscViewerASCIIPushTab(viewer));
1502         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1503         if (dm->fields[f].adjacency[0]) {
1504           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1505           else                            PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1506         } else {
1507           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1508           else                            PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1509         }
1510         PetscCall(PetscViewerASCIIPopTab(viewer));
1511       }
1512     }
1513     PetscCall(DMGetCoarseDM(dm, &cdm));
1514     if (cdm) {
1515       PetscCall(PetscViewerASCIIPushTab(viewer));
1516       PetscCall(DMPlexView_Ascii(cdm, viewer));
1517       PetscCall(PetscViewerASCIIPopTab(viewer));
1518     }
1519   }
1520   PetscFunctionReturn(0);
1521 }
1522 
1523 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1524 {
1525   DMPolytopeType ct;
1526   PetscMPIInt    rank;
1527   PetscInt       cdim;
1528 
1529   PetscFunctionBegin;
1530   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject) dm), &rank));
1531   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1532   PetscCall(DMGetCoordinateDim(dm, &cdim));
1533   switch (ct) {
1534   case DM_POLYTOPE_SEGMENT:
1535   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1536     switch (cdim) {
1537     case 1:
1538     {
1539       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1540       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1541 
1542       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y,    PetscRealPart(coords[1]), y,    PETSC_DRAW_BLACK));
1543       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y+dy, PetscRealPart(coords[0]), y-dy, PETSC_DRAW_BLACK));
1544       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y+dy, PetscRealPart(coords[1]), y-dy, PETSC_DRAW_BLACK));
1545     }
1546     break;
1547     case 2:
1548     {
1549       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1550       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1551       const PetscReal l  = 0.1/PetscSqrtReal(dx*dx + dy*dy);
1552 
1553       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1554       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));
1555       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));
1556     }
1557     break;
1558     default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1559     }
1560     break;
1561   case DM_POLYTOPE_TRIANGLE:
1562     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]),
1563                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1564                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1565                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2));
1566     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1567     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1568     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1569     break;
1570   case DM_POLYTOPE_QUADRILATERAL:
1571     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]),
1572                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1573                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1574                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2));
1575     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]),
1576                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1577                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2,
1578                               PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2));
1579     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1580     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1581     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1582     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1583     break;
1584   default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1585   }
1586   PetscFunctionReturn(0);
1587 }
1588 
1589 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1590 {
1591   DMPolytopeType ct;
1592   PetscReal      centroid[2] = {0., 0.};
1593   PetscMPIInt    rank;
1594   PetscInt       fillColor, v, e, d;
1595 
1596   PetscFunctionBegin;
1597   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject) dm), &rank));
1598   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1599   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS-2) + 2;
1600   switch (ct) {
1601   case DM_POLYTOPE_TRIANGLE:
1602     {
1603       PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1604 
1605       for (v = 0; v < 3; ++v) {centroid[0] += PetscRealPart(coords[v*2+0])/3.;centroid[1] += PetscRealPart(coords[v*2+1])/3.;}
1606       for (e = 0; e < 3; ++e) {
1607         refCoords[0] = refVertices[e*2+0];
1608         refCoords[1] = refVertices[e*2+1];
1609         for (d = 1; d <= edgeDiv; ++d) {
1610           refCoords[d*2+0] = refCoords[0] + (refVertices[(e+1)%3 * 2 + 0] - refCoords[0])*d/edgeDiv;
1611           refCoords[d*2+1] = refCoords[1] + (refVertices[(e+1)%3 * 2 + 1] - refCoords[1])*d/edgeDiv;
1612         }
1613         PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv+1, refCoords, edgeCoords));
1614         for (d = 0; d < edgeDiv; ++d) {
1615           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));
1616           PetscCall(PetscDrawLine(draw, edgeCoords[d*2+0], edgeCoords[d*2+1], edgeCoords[(d+1)*2+0], edgeCoords[(d+1)*2+1], PETSC_DRAW_BLACK));
1617         }
1618       }
1619     }
1620     break;
1621   default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1622   }
1623   PetscFunctionReturn(0);
1624 }
1625 
1626 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1627 {
1628   PetscDraw          draw;
1629   DM                 cdm;
1630   PetscSection       coordSection;
1631   Vec                coordinates;
1632   const PetscScalar *coords;
1633   PetscReal          xyl[2],xyr[2],bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
1634   PetscReal         *refCoords, *edgeCoords;
1635   PetscBool          isnull, drawAffine = PETSC_TRUE;
1636   PetscInt           dim, vStart, vEnd, cStart, cEnd, c, N, edgeDiv = 4;
1637 
1638   PetscFunctionBegin;
1639   PetscCall(DMGetCoordinateDim(dm, &dim));
1640   PetscCheck(dim <= 2,PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1641   PetscCall(PetscOptionsGetBool(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1642   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv+1)*dim, &refCoords, (edgeDiv+1)*dim, &edgeCoords));
1643   PetscCall(DMGetCoordinateDM(dm, &cdm));
1644   PetscCall(DMGetLocalSection(cdm, &coordSection));
1645   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1646   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1647   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1648 
1649   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1650   PetscCall(PetscDrawIsNull(draw, &isnull));
1651   if (isnull) PetscFunctionReturn(0);
1652   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1653 
1654   PetscCall(VecGetLocalSize(coordinates, &N));
1655   PetscCall(VecGetArrayRead(coordinates, &coords));
1656   for (c = 0; c < N; c += dim) {
1657     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));   bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
1658     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c+1])); bound[3] = PetscMax(bound[3], PetscRealPart(coords[c+1]));
1659   }
1660   PetscCall(VecRestoreArrayRead(coordinates, &coords));
1661   PetscCall(MPIU_Allreduce(&bound[0],xyl,2,MPIU_REAL,MPIU_MIN,PetscObjectComm((PetscObject)dm)));
1662   PetscCall(MPIU_Allreduce(&bound[2],xyr,2,MPIU_REAL,MPIU_MAX,PetscObjectComm((PetscObject)dm)));
1663   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1664   PetscCall(PetscDrawClear(draw));
1665 
1666   for (c = cStart; c < cEnd; ++c) {
1667     PetscScalar *coords = NULL;
1668     PetscInt     numCoords;
1669 
1670     PetscCall(DMPlexVecGetClosureAtDepth_Internal(dm, coordSection, coordinates, c, 0, &numCoords, &coords));
1671     if (drawAffine) {
1672       PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1673     } else {
1674       PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1675     }
1676     PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
1677   }
1678   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1679   PetscCall(PetscDrawFlush(draw));
1680   PetscCall(PetscDrawPause(draw));
1681   PetscCall(PetscDrawSave(draw));
1682   PetscFunctionReturn(0);
1683 }
1684 
1685 #if defined(PETSC_HAVE_EXODUSII)
1686 #include <exodusII.h>
1687 #include <petscviewerexodusii.h>
1688 #endif
1689 
1690 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1691 {
1692   PetscBool      iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus;
1693   char           name[PETSC_MAX_PATH_LEN];
1694 
1695   PetscFunctionBegin;
1696   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1697   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1698   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERASCII,    &iascii));
1699   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERVTK,      &isvtk));
1700   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,     &ishdf5));
1701   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERDRAW,     &isdraw));
1702   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERGLVIS,    &isglvis));
1703   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWEREXODUSII, &isexodus));
1704   if (iascii) {
1705     PetscViewerFormat format;
1706     PetscCall(PetscViewerGetFormat(viewer, &format));
1707     if (format == PETSC_VIEWER_ASCII_GLVIS) {
1708       PetscCall(DMPlexView_GLVis(dm, viewer));
1709     } else {
1710       PetscCall(DMPlexView_Ascii(dm, viewer));
1711     }
1712   } else if (ishdf5) {
1713 #if defined(PETSC_HAVE_HDF5)
1714     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1715 #else
1716     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1717 #endif
1718   } else if (isvtk) {
1719     PetscCall(DMPlexVTKWriteAll((PetscObject) dm,viewer));
1720   } else if (isdraw) {
1721     PetscCall(DMPlexView_Draw(dm, viewer));
1722   } else if (isglvis) {
1723     PetscCall(DMPlexView_GLVis(dm, viewer));
1724 #if defined(PETSC_HAVE_EXODUSII)
1725   } else if (isexodus) {
1726 /*
1727       exodusII requires that all sets be part of exactly one cell set.
1728       If the dm does not have a "Cell Sets" label defined, we create one
1729       with ID 1, containig all cells.
1730       Note that if the Cell Sets label is defined but does not cover all cells,
1731       we may still have a problem. This should probably be checked here or in the viewer;
1732     */
1733     PetscInt numCS;
1734     PetscCall(DMGetLabelSize(dm,"Cell Sets",&numCS));
1735     if (!numCS) {
1736       PetscInt cStart, cEnd, c;
1737       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1738       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1739       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1740     }
1741     PetscCall(DMView_PlexExodusII(dm, viewer));
1742 #endif
1743   } else {
1744     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1745   }
1746   /* Optionally view the partition */
1747   PetscCall(PetscOptionsHasName(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_partition_view", &flg));
1748   if (flg) {
1749     Vec ranks;
1750     PetscCall(DMPlexCreateRankField(dm, &ranks));
1751     PetscCall(VecView(ranks, viewer));
1752     PetscCall(VecDestroy(&ranks));
1753   }
1754   /* Optionally view a label */
1755   PetscCall(PetscOptionsGetString(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1756   if (flg) {
1757     DMLabel label;
1758     Vec     val;
1759 
1760     PetscCall(DMGetLabel(dm, name, &label));
1761     PetscCheck(label,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1762     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1763     PetscCall(VecView(val, viewer));
1764     PetscCall(VecDestroy(&val));
1765   }
1766   PetscFunctionReturn(0);
1767 }
1768 
1769 /*@
1770   DMPlexTopologyView - Saves a DMPlex topology into a file
1771 
1772   Collective on DM
1773 
1774   Input Parameters:
1775 + dm     - The DM whose topology is to be saved
1776 - viewer - The PetscViewer for saving
1777 
1778   Level: advanced
1779 
1780 .seealso: `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`
1781 @*/
1782 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
1783 {
1784   PetscBool      ishdf5;
1785 
1786   PetscFunctionBegin;
1787   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1788   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1789   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
1790   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView,viewer,0,0,0));
1791   if (ishdf5) {
1792 #if defined(PETSC_HAVE_HDF5)
1793     PetscViewerFormat format;
1794     PetscCall(PetscViewerGetFormat(viewer, &format));
1795     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1796       IS globalPointNumbering;
1797 
1798       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1799       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
1800       PetscCall(ISDestroy(&globalPointNumbering));
1801     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1802 #else
1803     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1804 #endif
1805   }
1806   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView,viewer,0,0,0));
1807   PetscFunctionReturn(0);
1808 }
1809 
1810 /*@
1811   DMPlexCoordinatesView - Saves DMPlex coordinates into a file
1812 
1813   Collective on DM
1814 
1815   Input Parameters:
1816 + dm     - The DM whose coordinates are to be saved
1817 - viewer - The PetscViewer for saving
1818 
1819   Level: advanced
1820 
1821 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`
1822 @*/
1823 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
1824 {
1825   PetscBool      ishdf5;
1826 
1827   PetscFunctionBegin;
1828   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1829   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1830   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
1831   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView,viewer,0,0,0));
1832   if (ishdf5) {
1833 #if defined(PETSC_HAVE_HDF5)
1834     PetscViewerFormat format;
1835     PetscCall(PetscViewerGetFormat(viewer, &format));
1836     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1837       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
1838     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1839 #else
1840     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1841 #endif
1842   }
1843   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView,viewer,0,0,0));
1844   PetscFunctionReturn(0);
1845 }
1846 
1847 /*@
1848   DMPlexLabelsView - Saves DMPlex labels into a file
1849 
1850   Collective on DM
1851 
1852   Input Parameters:
1853 + dm     - The DM whose labels are to be saved
1854 - viewer - The PetscViewer for saving
1855 
1856   Level: advanced
1857 
1858 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`
1859 @*/
1860 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
1861 {
1862   PetscBool      ishdf5;
1863 
1864   PetscFunctionBegin;
1865   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1866   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1867   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
1868   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView,viewer,0,0,0));
1869   if (ishdf5) {
1870 #if defined(PETSC_HAVE_HDF5)
1871     IS                globalPointNumbering;
1872     PetscViewerFormat format;
1873 
1874     PetscCall(PetscViewerGetFormat(viewer, &format));
1875     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1876       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1877       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
1878       PetscCall(ISDestroy(&globalPointNumbering));
1879     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1880 #else
1881     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1882 #endif
1883   }
1884   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView,viewer,0,0,0));
1885   PetscFunctionReturn(0);
1886 }
1887 
1888 /*@
1889   DMPlexSectionView - Saves a section associated with a DMPlex
1890 
1891   Collective on DM
1892 
1893   Input Parameters:
1894 + dm         - The DM that contains the topology on which the section to be saved is defined
1895 . viewer     - The PetscViewer for saving
1896 - sectiondm  - The DM that contains the section to be saved
1897 
1898   Level: advanced
1899 
1900   Notes:
1901   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.
1902 
1903   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.
1904 
1905 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`
1906 @*/
1907 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
1908 {
1909   PetscBool      ishdf5;
1910 
1911   PetscFunctionBegin;
1912   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1913   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1914   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1915   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
1916   PetscCall(PetscLogEventBegin(DMPLEX_SectionView,viewer,0,0,0));
1917   if (ishdf5) {
1918 #if defined(PETSC_HAVE_HDF5)
1919     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
1920 #else
1921     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1922 #endif
1923   }
1924   PetscCall(PetscLogEventEnd(DMPLEX_SectionView,viewer,0,0,0));
1925   PetscFunctionReturn(0);
1926 }
1927 
1928 /*@
1929   DMPlexGlobalVectorView - Saves a global vector
1930 
1931   Collective on DM
1932 
1933   Input Parameters:
1934 + dm        - The DM that represents the topology
1935 . viewer    - The PetscViewer to save data with
1936 . sectiondm - The DM that contains the global section on which vec is defined
1937 - vec       - The global vector to be saved
1938 
1939   Level: advanced
1940 
1941   Notes:
1942   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.
1943 
1944   Typical calling sequence
1945 $       DMCreate(PETSC_COMM_WORLD, &dm);
1946 $       DMSetType(dm, DMPLEX);
1947 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
1948 $       DMClone(dm, &sectiondm);
1949 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
1950 $       PetscSectionCreate(PETSC_COMM_WORLD, &section);
1951 $       DMPlexGetChart(sectiondm, &pStart, &pEnd);
1952 $       PetscSectionSetChart(section, pStart, pEnd);
1953 $       PetscSectionSetUp(section);
1954 $       DMSetLocalSection(sectiondm, section);
1955 $       PetscSectionDestroy(&section);
1956 $       DMGetGlobalVector(sectiondm, &vec);
1957 $       PetscObjectSetName((PetscObject)vec, "vec_name");
1958 $       DMPlexTopologyView(dm, viewer);
1959 $       DMPlexSectionView(dm, viewer, sectiondm);
1960 $       DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
1961 $       DMRestoreGlobalVector(sectiondm, &vec);
1962 $       DMDestroy(&sectiondm);
1963 $       DMDestroy(&dm);
1964 
1965 .seealso: `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
1966 @*/
1967 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
1968 {
1969   PetscBool       ishdf5;
1970 
1971   PetscFunctionBegin;
1972   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1973   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1974   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1975   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
1976   /* Check consistency */
1977   {
1978     PetscSection  section;
1979     PetscBool     includesConstraints;
1980     PetscInt      m, m1;
1981 
1982     PetscCall(VecGetLocalSize(vec, &m1));
1983     PetscCall(DMGetGlobalSection(sectiondm, &section));
1984     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
1985     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
1986     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
1987     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
1988   }
1989   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1990   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView,viewer,0,0,0));
1991   if (ishdf5) {
1992 #if defined(PETSC_HAVE_HDF5)
1993     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
1994 #else
1995     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1996 #endif
1997   }
1998   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView,viewer,0,0,0));
1999   PetscFunctionReturn(0);
2000 }
2001 
2002 /*@
2003   DMPlexLocalVectorView - Saves a local vector
2004 
2005   Collective on DM
2006 
2007   Input Parameters:
2008 + dm        - The DM that represents the topology
2009 . viewer    - The PetscViewer to save data with
2010 . sectiondm - The DM that contains the local section on which vec is defined; may be the same as dm
2011 - vec       - The local vector to be saved
2012 
2013   Level: advanced
2014 
2015   Notes:
2016   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.
2017 
2018   Typical calling sequence
2019 $       DMCreate(PETSC_COMM_WORLD, &dm);
2020 $       DMSetType(dm, DMPLEX);
2021 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2022 $       DMClone(dm, &sectiondm);
2023 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2024 $       PetscSectionCreate(PETSC_COMM_WORLD, &section);
2025 $       DMPlexGetChart(sectiondm, &pStart, &pEnd);
2026 $       PetscSectionSetChart(section, pStart, pEnd);
2027 $       PetscSectionSetUp(section);
2028 $       DMSetLocalSection(sectiondm, section);
2029 $       DMGetLocalVector(sectiondm, &vec);
2030 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2031 $       DMPlexTopologyView(dm, viewer);
2032 $       DMPlexSectionView(dm, viewer, sectiondm);
2033 $       DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2034 $       DMRestoreLocalVector(sectiondm, &vec);
2035 $       DMDestroy(&sectiondm);
2036 $       DMDestroy(&dm);
2037 
2038 .seealso: `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2039 @*/
2040 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2041 {
2042   PetscBool       ishdf5;
2043 
2044   PetscFunctionBegin;
2045   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2046   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2047   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2048   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2049   /* Check consistency */
2050   {
2051     PetscSection  section;
2052     PetscBool     includesConstraints;
2053     PetscInt      m, m1;
2054 
2055     PetscCall(VecGetLocalSize(vec, &m1));
2056     PetscCall(DMGetLocalSection(sectiondm, &section));
2057     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2058     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2059     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2060     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2061   }
2062   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2063   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView,viewer,0,0,0));
2064   if (ishdf5) {
2065 #if defined(PETSC_HAVE_HDF5)
2066     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2067 #else
2068     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2069 #endif
2070   }
2071   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView,viewer,0,0,0));
2072   PetscFunctionReturn(0);
2073 }
2074 
2075 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2076 {
2077   PetscBool      ishdf5;
2078 
2079   PetscFunctionBegin;
2080   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2081   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2082   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5,   &ishdf5));
2083   if (ishdf5) {
2084 #if defined(PETSC_HAVE_HDF5)
2085     PetscViewerFormat format;
2086     PetscCall(PetscViewerGetFormat(viewer, &format));
2087     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2088       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2089     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2090       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2091     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2092     PetscFunctionReturn(0);
2093 #else
2094     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2095 #endif
2096   } else SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2097 }
2098 
2099 /*@
2100   DMPlexTopologyLoad - Loads a topology into a DMPlex
2101 
2102   Collective on DM
2103 
2104   Input Parameters:
2105 + dm     - The DM into which the topology is loaded
2106 - viewer - The PetscViewer for the saved topology
2107 
2108   Output Parameters:
2109 . 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
2110 
2111   Level: advanced
2112 
2113 .seealso: `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2114 @*/
2115 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2116 {
2117   PetscBool      ishdf5;
2118 
2119   PetscFunctionBegin;
2120   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2121   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2122   if (globalToLocalPointSF) PetscValidPointer(globalToLocalPointSF, 3);
2123   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
2124   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad,viewer,0,0,0));
2125   if (ishdf5) {
2126 #if defined(PETSC_HAVE_HDF5)
2127     PetscViewerFormat format;
2128     PetscCall(PetscViewerGetFormat(viewer, &format));
2129     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2130       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2131     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2132 #else
2133     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2134 #endif
2135   }
2136   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad,viewer,0,0,0));
2137   PetscFunctionReturn(0);
2138 }
2139 
2140 /*@
2141   DMPlexCoordinatesLoad - Loads coordinates into a DMPlex
2142 
2143   Collective on DM
2144 
2145   Input Parameters:
2146 + dm     - The DM into which the coordinates are loaded
2147 . viewer - The PetscViewer for the saved coordinates
2148 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2149 
2150   Level: advanced
2151 
2152 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2153 @*/
2154 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2155 {
2156   PetscBool      ishdf5;
2157 
2158   PetscFunctionBegin;
2159   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2160   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2161   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2162   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
2163   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad,viewer,0,0,0));
2164   if (ishdf5) {
2165 #if defined(PETSC_HAVE_HDF5)
2166     PetscViewerFormat format;
2167     PetscCall(PetscViewerGetFormat(viewer, &format));
2168     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2169       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2170     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2171 #else
2172     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2173 #endif
2174   }
2175   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad,viewer,0,0,0));
2176   PetscFunctionReturn(0);
2177 }
2178 
2179 /*@
2180   DMPlexLabelsLoad - Loads labels into a DMPlex
2181 
2182   Collective on DM
2183 
2184   Input Parameters:
2185 + dm     - The DM into which the labels are loaded
2186 . viewer - The PetscViewer for the saved labels
2187 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2188 
2189   Level: advanced
2190 
2191   Notes:
2192   The PetscSF argument must not be NULL if the DM is distributed, otherwise an error occurs.
2193 
2194 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2195 @*/
2196 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2197 {
2198   PetscBool      ishdf5;
2199 
2200   PetscFunctionBegin;
2201   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2202   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2203   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2204   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERHDF5, &ishdf5));
2205   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad,viewer,0,0,0));
2206   if (ishdf5) {
2207 #if defined(PETSC_HAVE_HDF5)
2208     PetscViewerFormat format;
2209 
2210     PetscCall(PetscViewerGetFormat(viewer, &format));
2211     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2212       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2213     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2214 #else
2215     SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2216 #endif
2217   }
2218   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad,viewer,0,0,0));
2219   PetscFunctionReturn(0);
2220 }
2221 
2222 /*@
2223   DMPlexSectionLoad - Loads section into a DMPlex
2224 
2225   Collective on DM
2226 
2227   Input Parameters:
2228 + dm          - The DM that represents the topology
2229 . viewer      - The PetscViewer that represents the on-disk section (sectionA)
2230 . sectiondm   - The DM into which the on-disk section (sectionA) is migrated
2231 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2232 
2233   Output Parameters
2234 + 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)
2235 - 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)
2236 
2237   Level: advanced
2238 
2239   Notes:
2240   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.
2241 
2242   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.
2243 
2244   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.
2245 
2246   Example using 2 processes:
2247 $  NX (number of points on dm): 4
2248 $  sectionA                   : the on-disk section
2249 $  vecA                       : a vector associated with sectionA
2250 $  sectionB                   : sectiondm's local section constructed in this function
2251 $  vecB (local)               : a vector associated with sectiondm's local section
2252 $  vecB (global)              : a vector associated with sectiondm's global section
2253 $
2254 $                                     rank 0    rank 1
2255 $  vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2256 $  sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2257 $  sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2258 $  sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2259 $  [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2260 $  sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2261 $  sectionB->atlasDof             :     1 0 1 | 1 3
2262 $  sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2263 $  vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2264 $  vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2265 $
2266 $  where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2267 
2268 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`
2269 @*/
2270 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2271 {
2272   PetscBool      ishdf5;
2273 
2274   PetscFunctionBegin;
2275   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2276   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2277   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2278   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2279   if (globalDofSF) PetscValidPointer(globalDofSF, 5);
2280   if (localDofSF) PetscValidPointer(localDofSF, 6);
2281   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
2282   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad,viewer,0,0,0));
2283   if (ishdf5) {
2284 #if defined(PETSC_HAVE_HDF5)
2285     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2286 #else
2287     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2288 #endif
2289   }
2290   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad,viewer,0,0,0));
2291   PetscFunctionReturn(0);
2292 }
2293 
2294 /*@
2295   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2296 
2297   Collective on DM
2298 
2299   Input Parameters:
2300 + dm        - The DM that represents the topology
2301 . viewer    - The PetscViewer that represents the on-disk vector data
2302 . sectiondm - The DM that contains the global section on which vec is defined
2303 . sf        - The SF that migrates the on-disk vector data into vec
2304 - vec       - The global vector to set values of
2305 
2306   Level: advanced
2307 
2308   Notes:
2309   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.
2310 
2311   Typical calling sequence
2312 $       DMCreate(PETSC_COMM_WORLD, &dm);
2313 $       DMSetType(dm, DMPLEX);
2314 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2315 $       DMPlexTopologyLoad(dm, viewer, &sfX);
2316 $       DMClone(dm, &sectiondm);
2317 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2318 $       DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2319 $       DMGetGlobalVector(sectiondm, &vec);
2320 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2321 $       DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2322 $       DMRestoreGlobalVector(sectiondm, &vec);
2323 $       PetscSFDestroy(&gsf);
2324 $       PetscSFDestroy(&sfX);
2325 $       DMDestroy(&sectiondm);
2326 $       DMDestroy(&dm);
2327 
2328 .seealso: `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`
2329 @*/
2330 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2331 {
2332   PetscBool       ishdf5;
2333 
2334   PetscFunctionBegin;
2335   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2336   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2337   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2338   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2339   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2340   /* Check consistency */
2341   {
2342     PetscSection  section;
2343     PetscBool     includesConstraints;
2344     PetscInt      m, m1;
2345 
2346     PetscCall(VecGetLocalSize(vec, &m1));
2347     PetscCall(DMGetGlobalSection(sectiondm, &section));
2348     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2349     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2350     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2351     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2352   }
2353   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
2354   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad,viewer,0,0,0));
2355   if (ishdf5) {
2356 #if defined(PETSC_HAVE_HDF5)
2357     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2358 #else
2359     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2360 #endif
2361   }
2362   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad,viewer,0,0,0));
2363   PetscFunctionReturn(0);
2364 }
2365 
2366 /*@
2367   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2368 
2369   Collective on DM
2370 
2371   Input Parameters:
2372 + dm        - The DM that represents the topology
2373 . viewer    - The PetscViewer that represents the on-disk vector data
2374 . sectiondm - The DM that contains the local section on which vec is defined
2375 . sf        - The SF that migrates the on-disk vector data into vec
2376 - vec       - The local vector to set values of
2377 
2378   Level: advanced
2379 
2380   Notes:
2381   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.
2382 
2383   Typical calling sequence
2384 $       DMCreate(PETSC_COMM_WORLD, &dm);
2385 $       DMSetType(dm, DMPLEX);
2386 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2387 $       DMPlexTopologyLoad(dm, viewer, &sfX);
2388 $       DMClone(dm, &sectiondm);
2389 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2390 $       DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2391 $       DMGetLocalVector(sectiondm, &vec);
2392 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2393 $       DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2394 $       DMRestoreLocalVector(sectiondm, &vec);
2395 $       PetscSFDestroy(&lsf);
2396 $       PetscSFDestroy(&sfX);
2397 $       DMDestroy(&sectiondm);
2398 $       DMDestroy(&dm);
2399 
2400 .seealso: `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`
2401 @*/
2402 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2403 {
2404   PetscBool       ishdf5;
2405 
2406   PetscFunctionBegin;
2407   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2408   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2409   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2410   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2411   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2412   /* Check consistency */
2413   {
2414     PetscSection  section;
2415     PetscBool     includesConstraints;
2416     PetscInt      m, m1;
2417 
2418     PetscCall(VecGetLocalSize(vec, &m1));
2419     PetscCall(DMGetLocalSection(sectiondm, &section));
2420     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2421     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2422     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2423     PetscCheck(m1 == m,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2424   }
2425   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERHDF5,&ishdf5));
2426   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad,viewer,0,0,0));
2427   if (ishdf5) {
2428 #if defined(PETSC_HAVE_HDF5)
2429     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2430 #else
2431     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2432 #endif
2433   }
2434   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad,viewer,0,0,0));
2435   PetscFunctionReturn(0);
2436 }
2437 
2438 PetscErrorCode DMDestroy_Plex(DM dm)
2439 {
2440   DM_Plex       *mesh = (DM_Plex*) dm->data;
2441 
2442   PetscFunctionBegin;
2443   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMSetUpGLVisViewer_C",NULL));
2444   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexInsertBoundaryValues_C", NULL));
2445   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMCreateNeumannOverlap_C", NULL));
2446   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMInterpolateSolution_C", NULL));
2447   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexInsertTimeDerviativeBoundaryValues_C", NULL));
2448   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexGetOverlap_C", NULL));
2449   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexDistributeGetDefault_C", NULL));
2450   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexDistributeSetDefault_C", NULL));
2451   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"MatComputeNeumannOverlap_C",NULL));
2452   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexReorderGetDefault_C", NULL));
2453   PetscCall(PetscObjectComposeFunction((PetscObject)dm,"DMPlexReorderSetDefault_C", NULL));
2454   if (--mesh->refct > 0) PetscFunctionReturn(0);
2455   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2456   PetscCall(PetscFree(mesh->cones));
2457   PetscCall(PetscFree(mesh->coneOrientations));
2458   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2459   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2460   PetscCall(PetscFree(mesh->supports));
2461   PetscCall(PetscFree(mesh->facesTmp));
2462   PetscCall(PetscFree(mesh->tetgenOpts));
2463   PetscCall(PetscFree(mesh->triangleOpts));
2464   PetscCall(PetscFree(mesh->transformType));
2465   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2466   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2467   PetscCall(ISDestroy(&mesh->subpointIS));
2468   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2469   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2470   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2471   PetscCall(ISDestroy(&mesh->anchorIS));
2472   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2473   PetscCall(PetscFree(mesh->parents));
2474   PetscCall(PetscFree(mesh->childIDs));
2475   PetscCall(PetscSectionDestroy(&mesh->childSection));
2476   PetscCall(PetscFree(mesh->children));
2477   PetscCall(DMDestroy(&mesh->referenceTree));
2478   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2479   PetscCall(PetscFree(mesh->neighbors));
2480   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2481   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2482   PetscCall(PetscFree(mesh));
2483   PetscFunctionReturn(0);
2484 }
2485 
2486 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2487 {
2488   PetscSection           sectionGlobal;
2489   PetscInt               bs = -1, mbs;
2490   PetscInt               localSize, localStart = 0;
2491   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2492   MatType                mtype;
2493   ISLocalToGlobalMapping ltog;
2494 
2495   PetscFunctionBegin;
2496   PetscCall(MatInitializePackage());
2497   mtype = dm->mattype;
2498   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2499   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2500   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2501   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject) dm)));
2502   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2503   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2504   PetscCall(MatSetType(*J, mtype));
2505   PetscCall(MatSetFromOptions(*J));
2506   PetscCall(MatGetBlockSize(*J, &mbs));
2507   if (mbs > 1) bs = mbs;
2508   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2509   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2510   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2511   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2512   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2513   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2514   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2515   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2516   if (!isShell) {
2517     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2518     PetscInt  *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2519     PetscInt  pStart, pEnd, p, dof, cdof;
2520 
2521     PetscCall(DMGetLocalToGlobalMapping(dm,&ltog));
2522 
2523     PetscCall(PetscCalloc1(localSize, &pblocks));
2524     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2525     for (p = pStart; p < pEnd; ++p) {
2526       PetscInt bdof, offset;
2527 
2528       PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2529       PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2530       PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2531       for (PetscInt i=0; i < dof - cdof; i++)
2532         pblocks[offset - localStart + i] = dof - cdof;
2533       dof  = dof < 0 ? -(dof+1) : dof;
2534       bdof = cdof && (dof-cdof) ? 1 : dof;
2535       if (dof) {
2536         if (bs < 0)          {bs = bdof;}
2537         else if (bs != bdof) {bs = 1;}
2538       }
2539     }
2540     /* Must have same blocksize on all procs (some might have no points) */
2541     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2542     bsLocal[1] = bs;
2543     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject) dm), bsLocal, bsMinMax));
2544     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2545     else bs = bsMinMax[0];
2546     bs = PetscMax(1,bs);
2547     PetscCall(MatSetLocalToGlobalMapping(*J,ltog,ltog));
2548     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2549       PetscCall(MatSetBlockSize(*J, bs));
2550       PetscCall(MatSetUp(*J));
2551     } else {
2552       PetscCall(PetscCalloc4(localSize/bs, &dnz, localSize/bs, &onz, localSize/bs, &dnzu, localSize/bs, &onzu));
2553       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2554       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2555     }
2556     { // Consolidate blocks
2557       PetscInt nblocks = 0;
2558       for (PetscInt i=0; i<localSize; i += PetscMax(1, pblocks[i])) {
2559         if (pblocks[i] == 0) continue;
2560         pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2561         for (PetscInt j=1; j<pblocks[i]; j++) {
2562            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]);
2563         }
2564       }
2565       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2566     }
2567     PetscCall(PetscFree(pblocks));
2568   }
2569   PetscCall(MatSetDM(*J, dm));
2570   PetscFunctionReturn(0);
2571 }
2572 
2573 /*@
2574   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2575 
2576   Not collective
2577 
2578   Input Parameter:
2579 . mesh - The DMPlex
2580 
2581   Output Parameters:
2582 . subsection - The subdomain section
2583 
2584   Level: developer
2585 
2586 .seealso:
2587 @*/
2588 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2589 {
2590   DM_Plex       *mesh = (DM_Plex*) dm->data;
2591 
2592   PetscFunctionBegin;
2593   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2594   if (!mesh->subdomainSection) {
2595     PetscSection section;
2596     PetscSF      sf;
2597 
2598     PetscCall(PetscSFCreate(PETSC_COMM_SELF,&sf));
2599     PetscCall(DMGetLocalSection(dm,&section));
2600     PetscCall(PetscSectionCreateGlobalSection(section,sf,PETSC_FALSE,PETSC_TRUE,&mesh->subdomainSection));
2601     PetscCall(PetscSFDestroy(&sf));
2602   }
2603   *subsection = mesh->subdomainSection;
2604   PetscFunctionReturn(0);
2605 }
2606 
2607 /*@
2608   DMPlexGetChart - Return the interval for all mesh points [pStart, pEnd)
2609 
2610   Not collective
2611 
2612   Input Parameter:
2613 . mesh - The DMPlex
2614 
2615   Output Parameters:
2616 + pStart - The first mesh point
2617 - pEnd   - The upper bound for mesh points
2618 
2619   Level: beginner
2620 
2621 .seealso: `DMPlexCreate()`, `DMPlexSetChart()`
2622 @*/
2623 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2624 {
2625   DM_Plex       *mesh = (DM_Plex*) dm->data;
2626 
2627   PetscFunctionBegin;
2628   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2629   PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2630   PetscFunctionReturn(0);
2631 }
2632 
2633 /*@
2634   DMPlexSetChart - Set the interval for all mesh points [pStart, pEnd)
2635 
2636   Not collective
2637 
2638   Input Parameters:
2639 + mesh - The DMPlex
2640 . pStart - The first mesh point
2641 - pEnd   - The upper bound for mesh points
2642 
2643   Output Parameters:
2644 
2645   Level: beginner
2646 
2647 .seealso: `DMPlexCreate()`, `DMPlexGetChart()`
2648 @*/
2649 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2650 {
2651   DM_Plex       *mesh = (DM_Plex*) dm->data;
2652 
2653   PetscFunctionBegin;
2654   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2655   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2656   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2657   PetscFunctionReturn(0);
2658 }
2659 
2660 /*@
2661   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2662 
2663   Not collective
2664 
2665   Input Parameters:
2666 + mesh - The DMPlex
2667 - p - The point, which must lie in the chart set with DMPlexSetChart()
2668 
2669   Output Parameter:
2670 . size - The cone size for point p
2671 
2672   Level: beginner
2673 
2674 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2675 @*/
2676 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2677 {
2678   DM_Plex       *mesh = (DM_Plex*) dm->data;
2679 
2680   PetscFunctionBegin;
2681   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2682   PetscValidIntPointer(size, 3);
2683   PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2684   PetscFunctionReturn(0);
2685 }
2686 
2687 /*@
2688   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2689 
2690   Not collective
2691 
2692   Input Parameters:
2693 + mesh - The DMPlex
2694 . p - The point, which must lie in the chart set with DMPlexSetChart()
2695 - size - The cone size for point p
2696 
2697   Output Parameter:
2698 
2699   Note:
2700   This should be called after DMPlexSetChart().
2701 
2702   Level: beginner
2703 
2704 .seealso: `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2705 @*/
2706 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
2707 {
2708   DM_Plex       *mesh = (DM_Plex*) dm->data;
2709 
2710   PetscFunctionBegin;
2711   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2712   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
2713   PetscFunctionReturn(0);
2714 }
2715 
2716 /*@
2717   DMPlexAddConeSize - Add the given number of in-edges to this point in the DAG
2718 
2719   Not collective
2720 
2721   Input Parameters:
2722 + mesh - The DMPlex
2723 . p - The point, which must lie in the chart set with DMPlexSetChart()
2724 - size - The additional cone size for point p
2725 
2726   Output Parameter:
2727 
2728   Note:
2729   This should be called after DMPlexSetChart().
2730 
2731   Level: beginner
2732 
2733 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2734 @*/
2735 PetscErrorCode DMPlexAddConeSize(DM dm, PetscInt p, PetscInt size)
2736 {
2737   DM_Plex       *mesh = (DM_Plex*) dm->data;
2738   PetscFunctionBegin;
2739   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2740   PetscCall(PetscSectionAddDof(mesh->coneSection, p, size));
2741   PetscFunctionReturn(0);
2742 }
2743 
2744 /*@C
2745   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
2746 
2747   Not collective
2748 
2749   Input Parameters:
2750 + dm - The DMPlex
2751 - p - The point, which must lie in the chart set with DMPlexSetChart()
2752 
2753   Output Parameter:
2754 . cone - An array of points which are on the in-edges for point p
2755 
2756   Level: beginner
2757 
2758   Fortran Notes:
2759   Since it returns an array, this routine is only available in Fortran 90, and you must
2760   include petsc.h90 in your code.
2761   You must also call DMPlexRestoreCone() after you finish using the returned array.
2762   DMPlexRestoreCone() is not needed/available in C.
2763 
2764 .seealso: `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`
2765 @*/
2766 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
2767 {
2768   DM_Plex       *mesh = (DM_Plex*) dm->data;
2769   PetscInt       off;
2770 
2771   PetscFunctionBegin;
2772   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2773   PetscValidPointer(cone, 3);
2774   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
2775   *cone = &mesh->cones[off];
2776   PetscFunctionReturn(0);
2777 }
2778 
2779 /*@C
2780   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
2781 
2782   Not collective
2783 
2784   Input Parameters:
2785 + dm - The DMPlex
2786 - p - The IS of points, which must lie in the chart set with DMPlexSetChart()
2787 
2788   Output Parameters:
2789 + pConesSection - PetscSection describing the layout of pCones
2790 - pCones - An array of points which are on the in-edges for the point set p
2791 
2792   Level: intermediate
2793 
2794 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`
2795 @*/
2796 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
2797 {
2798   PetscSection        cs, newcs;
2799   PetscInt            *cones;
2800   PetscInt            *newarr=NULL;
2801   PetscInt            n;
2802 
2803   PetscFunctionBegin;
2804   PetscCall(DMPlexGetCones(dm, &cones));
2805   PetscCall(DMPlexGetConeSection(dm, &cs));
2806   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void**)&newarr) : NULL));
2807   if (pConesSection) *pConesSection = newcs;
2808   if (pCones) {
2809     PetscCall(PetscSectionGetStorageSize(newcs, &n));
2810     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
2811   }
2812   PetscFunctionReturn(0);
2813 }
2814 
2815 /*@
2816   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
2817 
2818   Not collective
2819 
2820   Input Parameters:
2821 + dm - The DMPlex
2822 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2823 
2824   Output Parameter:
2825 . expandedPoints - An array of vertices recursively expanded from input points
2826 
2827   Level: advanced
2828 
2829   Notes:
2830   Like DMPlexGetConeRecursive but returns only the 0-depth IS (i.e. vertices only) and no sections.
2831   There is no corresponding Restore function, just call ISDestroy() on the returned IS to deallocate.
2832 
2833 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetDepth()`
2834 @*/
2835 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
2836 {
2837   IS                  *expandedPointsAll;
2838   PetscInt            depth;
2839 
2840   PetscFunctionBegin;
2841   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2842   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2843   PetscValidPointer(expandedPoints, 3);
2844   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2845   *expandedPoints = expandedPointsAll[0];
2846   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
2847   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2848   PetscFunctionReturn(0);
2849 }
2850 
2851 /*@
2852   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).
2853 
2854   Not collective
2855 
2856   Input Parameters:
2857 + dm - The DMPlex
2858 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2859 
2860   Output Parameters:
2861 + depth - (optional) Size of the output arrays, equal to DMPlex depth, returned by DMPlexGetDepth()
2862 . expandedPoints - (optional) An array of index sets with recursively expanded cones
2863 - sections - (optional) An array of sections which describe mappings from points to their cone points
2864 
2865   Level: advanced
2866 
2867   Notes:
2868   Like DMPlexGetConeTuple() but recursive.
2869 
2870   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.
2871   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
2872 
2873   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:
2874   (1) DAG points in expandedPoints[d+1] with depth d+1 to their cone points in expandedPoints[d];
2875   (2) DAG points in expandedPoints[d+1] with depth in [0,d] to the same points in expandedPoints[d].
2876 
2877 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`, `DMPlexGetDepth()`
2878 @*/
2879 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2880 {
2881   const PetscInt      *arr0=NULL, *cone=NULL;
2882   PetscInt            *arr=NULL, *newarr=NULL;
2883   PetscInt            d, depth_, i, n, newn, cn, co, start, end;
2884   IS                  *expandedPoints_;
2885   PetscSection        *sections_;
2886 
2887   PetscFunctionBegin;
2888   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2889   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2890   if (depth) PetscValidIntPointer(depth, 3);
2891   if (expandedPoints) PetscValidPointer(expandedPoints, 4);
2892   if (sections) PetscValidPointer(sections, 5);
2893   PetscCall(ISGetLocalSize(points, &n));
2894   PetscCall(ISGetIndices(points, &arr0));
2895   PetscCall(DMPlexGetDepth(dm, &depth_));
2896   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
2897   PetscCall(PetscCalloc1(depth_, &sections_));
2898   arr = (PetscInt*) arr0; /* this is ok because first generation of arr is not modified */
2899   for (d=depth_-1; d>=0; d--) {
2900     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
2901     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
2902     for (i=0; i<n; i++) {
2903       PetscCall(DMPlexGetDepthStratum(dm, d+1, &start, &end));
2904       if (arr[i] >= start && arr[i] < end) {
2905         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
2906         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
2907       } else {
2908         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
2909       }
2910     }
2911     PetscCall(PetscSectionSetUp(sections_[d]));
2912     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
2913     PetscCall(PetscMalloc1(newn, &newarr));
2914     for (i=0; i<n; i++) {
2915       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
2916       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
2917       if (cn > 1) {
2918         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
2919         PetscCall(PetscMemcpy(&newarr[co], cone, cn*sizeof(PetscInt)));
2920       } else {
2921         newarr[co] = arr[i];
2922       }
2923     }
2924     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
2925     arr = newarr;
2926     n = newn;
2927   }
2928   PetscCall(ISRestoreIndices(points, &arr0));
2929   *depth = depth_;
2930   if (expandedPoints) *expandedPoints = expandedPoints_;
2931   else {
2932     for (d=0; d<depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
2933     PetscCall(PetscFree(expandedPoints_));
2934   }
2935   if (sections) *sections = sections_;
2936   else {
2937     for (d=0; d<depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
2938     PetscCall(PetscFree(sections_));
2939   }
2940   PetscFunctionReturn(0);
2941 }
2942 
2943 /*@
2944   DMPlexRestoreConeRecursive - Deallocates arrays created by DMPlexGetConeRecursive
2945 
2946   Not collective
2947 
2948   Input Parameters:
2949 + dm - The DMPlex
2950 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2951 
2952   Output Parameters:
2953 + depth - (optional) Size of the output arrays, equal to DMPlex depth, returned by DMPlexGetDepth()
2954 . expandedPoints - (optional) An array of recursively expanded cones
2955 - sections - (optional) An array of sections which describe mappings from points to their cone points
2956 
2957   Level: advanced
2958 
2959   Notes:
2960   See DMPlexGetConeRecursive() for details.
2961 
2962 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`, `DMPlexGetDepth()`
2963 @*/
2964 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2965 {
2966   PetscInt            d, depth_;
2967 
2968   PetscFunctionBegin;
2969   PetscCall(DMPlexGetDepth(dm, &depth_));
2970   PetscCheck(!depth || *depth == depth_,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
2971   if (depth) *depth = 0;
2972   if (expandedPoints) {
2973     for (d=0; d<depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
2974     PetscCall(PetscFree(*expandedPoints));
2975   }
2976   if (sections)  {
2977     for (d=0; d<depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
2978     PetscCall(PetscFree(*sections));
2979   }
2980   PetscFunctionReturn(0);
2981 }
2982 
2983 /*@
2984   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
2985 
2986   Not collective
2987 
2988   Input Parameters:
2989 + mesh - The DMPlex
2990 . p - The point, which must lie in the chart set with DMPlexSetChart()
2991 - cone - An array of points which are on the in-edges for point p
2992 
2993   Output Parameter:
2994 
2995   Note:
2996   This should be called after all calls to DMPlexSetConeSize() and DMSetUp().
2997 
2998   Level: beginner
2999 
3000 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3001 @*/
3002 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3003 {
3004   DM_Plex       *mesh = (DM_Plex*) dm->data;
3005   PetscInt       pStart, pEnd;
3006   PetscInt       dof, off, c;
3007 
3008   PetscFunctionBegin;
3009   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3010   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3011   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3012   if (dof) PetscValidIntPointer(cone, 3);
3013   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3014   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);
3015   for (c = 0; c < dof; ++c) {
3016     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);
3017     mesh->cones[off+c] = cone[c];
3018   }
3019   PetscFunctionReturn(0);
3020 }
3021 
3022 /*@C
3023   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3024 
3025   Not collective
3026 
3027   Input Parameters:
3028 + mesh - The DMPlex
3029 - p - The point, which must lie in the chart set with DMPlexSetChart()
3030 
3031   Output Parameter:
3032 . coneOrientation - An array of orientations which are on the in-edges for point p. An orientation is an
3033                     integer giving the prescription for cone traversal.
3034 
3035   Level: beginner
3036 
3037   Notes:
3038   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3039   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3040   of o, however it is not necessarily the inverse. To get the inverse, use DMPolytopeTypeComposeOrientationInv()
3041   with the identity.
3042 
3043   Fortran Notes:
3044   Since it returns an array, this routine is only available in Fortran 90, and you must
3045   include petsc.h90 in your code.
3046   You must also call DMPlexRestoreConeOrientation() after you finish using the returned array.
3047   DMPlexRestoreConeOrientation() is not needed/available in C.
3048 
3049 .seealso: `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3050 @*/
3051 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3052 {
3053   DM_Plex       *mesh = (DM_Plex*) dm->data;
3054   PetscInt       off;
3055 
3056   PetscFunctionBegin;
3057   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3058   if (PetscDefined(USE_DEBUG)) {
3059     PetscInt dof;
3060     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3061     if (dof) PetscValidPointer(coneOrientation, 3);
3062   }
3063   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3064 
3065   *coneOrientation = &mesh->coneOrientations[off];
3066   PetscFunctionReturn(0);
3067 }
3068 
3069 /*@
3070   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3071 
3072   Not collective
3073 
3074   Input Parameters:
3075 + mesh - The DMPlex
3076 . p - The point, which must lie in the chart set with DMPlexSetChart()
3077 - coneOrientation - An array of orientations
3078   Output Parameter:
3079 
3080   Notes:
3081   This should be called after all calls to DMPlexSetConeSize() and DMSetUp().
3082 
3083   The meaning of coneOrientation is detailed in DMPlexGetConeOrientation().
3084 
3085   Level: beginner
3086 
3087 .seealso: `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3088 @*/
3089 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3090 {
3091   DM_Plex       *mesh = (DM_Plex*) dm->data;
3092   PetscInt       pStart, pEnd;
3093   PetscInt       dof, off, c;
3094 
3095   PetscFunctionBegin;
3096   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3097   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3098   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3099   if (dof) PetscValidIntPointer(coneOrientation, 3);
3100   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3101   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);
3102   for (c = 0; c < dof; ++c) {
3103     PetscInt cdof, o = coneOrientation[c];
3104 
3105     PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off+c], &cdof));
3106     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);
3107     mesh->coneOrientations[off+c] = o;
3108   }
3109   PetscFunctionReturn(0);
3110 }
3111 
3112 /*@
3113   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3114 
3115   Not collective
3116 
3117   Input Parameters:
3118 + mesh - The DMPlex
3119 . p - The point, which must lie in the chart set with DMPlexSetChart()
3120 . conePos - The local index in the cone where the point should be put
3121 - conePoint - The mesh point to insert
3122 
3123   Level: beginner
3124 
3125 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3126 @*/
3127 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3128 {
3129   DM_Plex       *mesh = (DM_Plex*) dm->data;
3130   PetscInt       pStart, pEnd;
3131   PetscInt       dof, off;
3132 
3133   PetscFunctionBegin;
3134   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3135   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3136   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);
3137   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);
3138   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3139   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3140   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);
3141   mesh->cones[off+conePos] = conePoint;
3142   PetscFunctionReturn(0);
3143 }
3144 
3145 /*@
3146   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3147 
3148   Not collective
3149 
3150   Input Parameters:
3151 + mesh - The DMPlex
3152 . p - The point, which must lie in the chart set with DMPlexSetChart()
3153 . conePos - The local index in the cone where the point should be put
3154 - coneOrientation - The point orientation to insert
3155 
3156   Level: beginner
3157 
3158   Notes:
3159   The meaning of coneOrientation values is detailed in DMPlexGetConeOrientation().
3160 
3161 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3162 @*/
3163 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3164 {
3165   DM_Plex       *mesh = (DM_Plex*) dm->data;
3166   PetscInt       pStart, pEnd;
3167   PetscInt       dof, off;
3168 
3169   PetscFunctionBegin;
3170   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3171   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3172   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);
3173   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3174   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3175   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);
3176   mesh->coneOrientations[off+conePos] = coneOrientation;
3177   PetscFunctionReturn(0);
3178 }
3179 
3180 /*@
3181   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3182 
3183   Not collective
3184 
3185   Input Parameters:
3186 + mesh - The DMPlex
3187 - p - The point, which must lie in the chart set with DMPlexSetChart()
3188 
3189   Output Parameter:
3190 . size - The support size for point p
3191 
3192   Level: beginner
3193 
3194 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3195 @*/
3196 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3197 {
3198   DM_Plex       *mesh = (DM_Plex*) dm->data;
3199 
3200   PetscFunctionBegin;
3201   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3202   PetscValidIntPointer(size, 3);
3203   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3204   PetscFunctionReturn(0);
3205 }
3206 
3207 /*@
3208   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3209 
3210   Not collective
3211 
3212   Input Parameters:
3213 + mesh - The DMPlex
3214 . p - The point, which must lie in the chart set with DMPlexSetChart()
3215 - size - The support size for point p
3216 
3217   Output Parameter:
3218 
3219   Note:
3220   This should be called after DMPlexSetChart().
3221 
3222   Level: beginner
3223 
3224 .seealso: `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3225 @*/
3226 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3227 {
3228   DM_Plex       *mesh = (DM_Plex*) dm->data;
3229 
3230   PetscFunctionBegin;
3231   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3232   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3233   PetscFunctionReturn(0);
3234 }
3235 
3236 /*@C
3237   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3238 
3239   Not collective
3240 
3241   Input Parameters:
3242 + mesh - The DMPlex
3243 - p - The point, which must lie in the chart set with DMPlexSetChart()
3244 
3245   Output Parameter:
3246 . support - An array of points which are on the out-edges for point p
3247 
3248   Level: beginner
3249 
3250   Fortran Notes:
3251   Since it returns an array, this routine is only available in Fortran 90, and you must
3252   include petsc.h90 in your code.
3253   You must also call DMPlexRestoreSupport() after you finish using the returned array.
3254   DMPlexRestoreSupport() is not needed/available in C.
3255 
3256 .seealso: `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3257 @*/
3258 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3259 {
3260   DM_Plex       *mesh = (DM_Plex*) dm->data;
3261   PetscInt       off;
3262 
3263   PetscFunctionBegin;
3264   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3265   PetscValidPointer(support, 3);
3266   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3267   *support = &mesh->supports[off];
3268   PetscFunctionReturn(0);
3269 }
3270 
3271 /*@
3272   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3273 
3274   Not collective
3275 
3276   Input Parameters:
3277 + mesh - The DMPlex
3278 . p - The point, which must lie in the chart set with DMPlexSetChart()
3279 - support - An array of points which are on the out-edges for point p
3280 
3281   Output Parameter:
3282 
3283   Note:
3284   This should be called after all calls to DMPlexSetSupportSize() and DMSetUp().
3285 
3286   Level: beginner
3287 
3288 .seealso: `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3289 @*/
3290 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3291 {
3292   DM_Plex       *mesh = (DM_Plex*) dm->data;
3293   PetscInt       pStart, pEnd;
3294   PetscInt       dof, off, c;
3295 
3296   PetscFunctionBegin;
3297   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3298   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3299   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3300   if (dof) PetscValidIntPointer(support, 3);
3301   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3302   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);
3303   for (c = 0; c < dof; ++c) {
3304     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);
3305     mesh->supports[off+c] = support[c];
3306   }
3307   PetscFunctionReturn(0);
3308 }
3309 
3310 /*@
3311   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3312 
3313   Not collective
3314 
3315   Input Parameters:
3316 + mesh - The DMPlex
3317 . p - The point, which must lie in the chart set with DMPlexSetChart()
3318 . supportPos - The local index in the cone where the point should be put
3319 - supportPoint - The mesh point to insert
3320 
3321   Level: beginner
3322 
3323 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3324 @*/
3325 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3326 {
3327   DM_Plex       *mesh = (DM_Plex*) dm->data;
3328   PetscInt       pStart, pEnd;
3329   PetscInt       dof, off;
3330 
3331   PetscFunctionBegin;
3332   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3333   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3334   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3335   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3336   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);
3337   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);
3338   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);
3339   mesh->supports[off+supportPos] = supportPoint;
3340   PetscFunctionReturn(0);
3341 }
3342 
3343 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3344 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3345 {
3346   switch (ct) {
3347     case DM_POLYTOPE_SEGMENT:
3348       if (o == -1) return -2;
3349       break;
3350     case DM_POLYTOPE_TRIANGLE:
3351       if (o == -3) return -1;
3352       if (o == -2) return -3;
3353       if (o == -1) return -2;
3354       break;
3355     case DM_POLYTOPE_QUADRILATERAL:
3356       if (o == -4) return -2;
3357       if (o == -3) return -1;
3358       if (o == -2) return -4;
3359       if (o == -1) return -3;
3360       break;
3361     default: return o;
3362   }
3363   return o;
3364 }
3365 
3366 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3367 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3368 {
3369   switch (ct) {
3370     case DM_POLYTOPE_SEGMENT:
3371       if ((o == -2) || (o == 1)) return -1;
3372       if (o == -1) return 0;
3373       break;
3374     case DM_POLYTOPE_TRIANGLE:
3375       if (o == -3) return -2;
3376       if (o == -2) return -1;
3377       if (o == -1) return -3;
3378       break;
3379     case DM_POLYTOPE_QUADRILATERAL:
3380       if (o == -4) return -2;
3381       if (o == -3) return -1;
3382       if (o == -2) return -4;
3383       if (o == -1) return -3;
3384       break;
3385     default: return o;
3386   }
3387   return o;
3388 }
3389 
3390 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3391 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3392 {
3393   PetscInt       pStart, pEnd, p;
3394 
3395   PetscFunctionBegin;
3396   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3397   for (p = pStart; p < pEnd; ++p) {
3398     const PetscInt *cone, *ornt;
3399     PetscInt        coneSize, c;
3400 
3401     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3402     PetscCall(DMPlexGetCone(dm, p, &cone));
3403     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3404     for (c = 0; c < coneSize; ++c) {
3405       DMPolytopeType ct;
3406       const PetscInt o = ornt[c];
3407 
3408       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3409       switch (ct) {
3410         case DM_POLYTOPE_SEGMENT:
3411           if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3412           if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3413           break;
3414         case DM_POLYTOPE_TRIANGLE:
3415           if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3416           if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3417           if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3418           break;
3419         case DM_POLYTOPE_QUADRILATERAL:
3420           if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3421           if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3422           if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3423           if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3424           break;
3425         default: break;
3426       }
3427     }
3428   }
3429   PetscFunctionReturn(0);
3430 }
3431 
3432 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3433 {
3434   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3435   PetscInt       *closure;
3436   const PetscInt *tmp = NULL, *tmpO = NULL;
3437   PetscInt        off = 0, tmpSize, t;
3438 
3439   PetscFunctionBeginHot;
3440   if (ornt) {
3441     PetscCall(DMPlexGetCellType(dm, p, &ct));
3442     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3443   }
3444   if (*points) {
3445     closure = *points;
3446   } else {
3447     PetscInt maxConeSize, maxSupportSize;
3448     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3449     PetscCall(DMGetWorkArray(dm, 2*(PetscMax(maxConeSize, maxSupportSize)+1), MPIU_INT, &closure));
3450   }
3451   if (useCone) {
3452     PetscCall(DMPlexGetConeSize(dm, p, &tmpSize));
3453     PetscCall(DMPlexGetCone(dm, p, &tmp));
3454     PetscCall(DMPlexGetConeOrientation(dm, p, &tmpO));
3455   } else {
3456     PetscCall(DMPlexGetSupportSize(dm, p, &tmpSize));
3457     PetscCall(DMPlexGetSupport(dm, p, &tmp));
3458   }
3459   if (ct == DM_POLYTOPE_UNKNOWN) {
3460     closure[off++] = p;
3461     closure[off++] = 0;
3462     for (t = 0; t < tmpSize; ++t) {
3463       closure[off++] = tmp[t];
3464       closure[off++] = tmpO ? tmpO[t] : 0;
3465     }
3466   } else {
3467     const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, ornt);
3468 
3469     /* We assume that cells with a valid type have faces with a valid type */
3470     closure[off++] = p;
3471     closure[off++] = ornt;
3472     for (t = 0; t < tmpSize; ++t) {
3473       DMPolytopeType ft;
3474 
3475       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3476       closure[off++] = tmp[arr[t]];
3477       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3478     }
3479   }
3480   if (numPoints) *numPoints = tmpSize+1;
3481   if (points)    *points    = closure;
3482   PetscFunctionReturn(0);
3483 }
3484 
3485 /* We need a special tensor verison becasue we want to allow duplicate points in the endcaps for hybrid cells */
3486 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3487 {
3488   const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, o);
3489   const PetscInt *cone, *ornt;
3490   PetscInt       *pts,  *closure = NULL;
3491   DMPolytopeType  ft;
3492   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3493   PetscInt        dim, coneSize, c, d, clSize, cl;
3494 
3495   PetscFunctionBeginHot;
3496   PetscCall(DMGetDimension(dm, &dim));
3497   PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
3498   PetscCall(DMPlexGetCone(dm, point, &cone));
3499   PetscCall(DMPlexGetConeOrientation(dm, point, &ornt));
3500   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3501   coneSeries    = (maxConeSize    > 1) ? ((PetscPowInt(maxConeSize,    dim+1)-1)/(maxConeSize-1))    : dim+1;
3502   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim+1)-1)/(maxSupportSize-1)) : dim+1;
3503   maxSize       = PetscMax(coneSeries, supportSeries);
3504   if (*points) {pts  = *points;}
3505   else         PetscCall(DMGetWorkArray(dm, 2*maxSize, MPIU_INT, &pts));
3506   c    = 0;
3507   pts[c++] = point;
3508   pts[c++] = o;
3509   PetscCall(DMPlexGetCellType(dm, cone[arr[0*2+0]], &ft));
3510   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0*2+0]], DMPolytopeTypeComposeOrientation(ft, arr[0*2+1], ornt[0]), useCone, &clSize, &closure));
3511   for (cl = 0; cl < clSize*2; cl += 2) {pts[c++] = closure[cl]; pts[c++] = closure[cl+1];}
3512   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1*2+0]], DMPolytopeTypeComposeOrientation(ft, arr[1*2+1], ornt[1]), useCone, &clSize, &closure));
3513   for (cl = 0; cl < clSize*2; cl += 2) {pts[c++] = closure[cl]; pts[c++] = closure[cl+1];}
3514   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3515   for (d = 2; d < coneSize; ++d) {
3516     PetscCall(DMPlexGetCellType(dm, cone[arr[d*2+0]], &ft));
3517     pts[c++] = cone[arr[d*2+0]];
3518     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d*2+1], ornt[d]);
3519   }
3520   if (dim >= 3) {
3521     for (d = 2; d < coneSize; ++d) {
3522       const PetscInt  fpoint = cone[arr[d*2+0]];
3523       const PetscInt *fcone, *fornt;
3524       PetscInt        fconeSize, fc, i;
3525 
3526       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3527       const PetscInt *farr = DMPolytopeTypeGetArrangment(ft, DMPolytopeTypeComposeOrientation(ft, arr[d*2+1], ornt[d]));
3528       PetscCall(DMPlexGetConeSize(dm, fpoint, &fconeSize));
3529       PetscCall(DMPlexGetCone(dm, fpoint, &fcone));
3530       PetscCall(DMPlexGetConeOrientation(dm, fpoint, &fornt));
3531       for (fc = 0; fc < fconeSize; ++fc) {
3532         const PetscInt cp = fcone[farr[fc*2+0]];
3533         const PetscInt co = farr[fc*2+1];
3534 
3535         for (i = 0; i < c; i += 2) if (pts[i] == cp) break;
3536         if (i == c) {
3537           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3538           pts[c++] = cp;
3539           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc*2+0]]);
3540         }
3541       }
3542     }
3543   }
3544   *numPoints = c/2;
3545   *points    = pts;
3546   PetscFunctionReturn(0);
3547 }
3548 
3549 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3550 {
3551   DMPolytopeType ct;
3552   PetscInt      *closure, *fifo;
3553   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3554   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3555   PetscInt       depth, maxSize;
3556 
3557   PetscFunctionBeginHot;
3558   PetscCall(DMPlexGetDepth(dm, &depth));
3559   if (depth == 1) {
3560     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3561     PetscFunctionReturn(0);
3562   }
3563   PetscCall(DMPlexGetCellType(dm, p, &ct));
3564   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3565   if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
3566     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3567     PetscFunctionReturn(0);
3568   }
3569   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3570   coneSeries    = (maxConeSize    > 1) ? ((PetscPowInt(maxConeSize,    depth+1)-1)/(maxConeSize-1))    : depth+1;
3571   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth+1)-1)/(maxSupportSize-1)) : depth+1;
3572   maxSize       = PetscMax(coneSeries, supportSeries);
3573   PetscCall(DMGetWorkArray(dm, 3*maxSize, MPIU_INT, &fifo));
3574   if (*points) {closure = *points;}
3575   else         PetscCall(DMGetWorkArray(dm, 2*maxSize, MPIU_INT, &closure));
3576   closure[closureSize++] = p;
3577   closure[closureSize++] = ornt;
3578   fifo[fifoSize++]       = p;
3579   fifo[fifoSize++]       = ornt;
3580   fifo[fifoSize++]       = ct;
3581   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3582   while (fifoSize - fifoStart) {
3583     const PetscInt       q    = fifo[fifoStart++];
3584     const PetscInt       o    = fifo[fifoStart++];
3585     const DMPolytopeType qt   = (DMPolytopeType) fifo[fifoStart++];
3586     const PetscInt      *qarr = DMPolytopeTypeGetArrangment(qt, o);
3587     const PetscInt      *tmp, *tmpO;
3588     PetscInt             tmpSize, t;
3589 
3590     if (PetscDefined(USE_DEBUG)) {
3591       PetscInt nO = DMPolytopeTypeGetNumArrangments(qt)/2;
3592       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);
3593     }
3594     if (useCone) {
3595       PetscCall(DMPlexGetConeSize(dm, q, &tmpSize));
3596       PetscCall(DMPlexGetCone(dm, q, &tmp));
3597       PetscCall(DMPlexGetConeOrientation(dm, q, &tmpO));
3598     } else {
3599       PetscCall(DMPlexGetSupportSize(dm, q, &tmpSize));
3600       PetscCall(DMPlexGetSupport(dm, q, &tmp));
3601       tmpO = NULL;
3602     }
3603     for (t = 0; t < tmpSize; ++t) {
3604       const PetscInt ip = useCone && qarr ? qarr[t*2]   : t;
3605       const PetscInt io = useCone && qarr ? qarr[t*2+1] : 0;
3606       const PetscInt cp = tmp[ip];
3607       PetscCall(DMPlexGetCellType(dm, cp, &ct));
3608       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
3609       PetscInt       c;
3610 
3611       /* Check for duplicate */
3612       for (c = 0; c < closureSize; c += 2) {
3613         if (closure[c] == cp) break;
3614       }
3615       if (c == closureSize) {
3616         closure[closureSize++] = cp;
3617         closure[closureSize++] = co;
3618         fifo[fifoSize++]       = cp;
3619         fifo[fifoSize++]       = co;
3620         fifo[fifoSize++]       = ct;
3621       }
3622     }
3623   }
3624   PetscCall(DMRestoreWorkArray(dm, 3*maxSize, MPIU_INT, &fifo));
3625   if (numPoints) *numPoints = closureSize/2;
3626   if (points)    *points    = closure;
3627   PetscFunctionReturn(0);
3628 }
3629 
3630 /*@C
3631   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
3632 
3633   Not collective
3634 
3635   Input Parameters:
3636 + dm      - The DMPlex
3637 . p       - The mesh point
3638 - useCone - PETSC_TRUE for the closure, otherwise return the star
3639 
3640   Input/Output Parameter:
3641 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
3642            if NULL on input, internal storage will be returned, otherwise the provided array is used
3643 
3644   Output Parameter:
3645 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3646 
3647   Note:
3648   If using internal storage (points is NULL on input), each call overwrites the last output.
3649 
3650   Fortran Notes:
3651   Since it returns an array, this routine is only available in Fortran 90, and you must include petsc.h90 in your code.
3652 
3653   The numPoints argument is not present in the Fortran 90 binding since it is internal to the array.
3654 
3655   Level: beginner
3656 
3657 .seealso: `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3658 @*/
3659 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3660 {
3661   PetscFunctionBeginHot;
3662   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3663   if (numPoints) PetscValidIntPointer(numPoints, 4);
3664   if (points)    PetscValidPointer(points, 5);
3665   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
3666   PetscFunctionReturn(0);
3667 }
3668 
3669 /*@C
3670   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
3671 
3672   Not collective
3673 
3674   Input Parameters:
3675 + dm        - The DMPlex
3676 . p         - The mesh point
3677 . useCone   - PETSC_TRUE for the closure, otherwise return the star
3678 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3679 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
3680 
3681   Note:
3682   If not using internal storage (points is not NULL on input), this call is unnecessary
3683 
3684   Fortran Notes:
3685   Since it returns an array, this routine is only available in Fortran 90, and you must include petsc.h90 in your code.
3686 
3687   The numPoints argument is not present in the Fortran 90 binding since it is internal to the array.
3688 
3689   Level: beginner
3690 
3691 .seealso: `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3692 @*/
3693 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3694 {
3695   PetscFunctionBeginHot;
3696   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3697   if (numPoints) *numPoints = 0;
3698   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
3699   PetscFunctionReturn(0);
3700 }
3701 
3702 /*@
3703   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
3704 
3705   Not collective
3706 
3707   Input Parameter:
3708 . mesh - The DMPlex
3709 
3710   Output Parameters:
3711 + maxConeSize - The maximum number of in-edges
3712 - maxSupportSize - The maximum number of out-edges
3713 
3714   Level: beginner
3715 
3716 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3717 @*/
3718 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
3719 {
3720   DM_Plex *mesh = (DM_Plex*) dm->data;
3721 
3722   PetscFunctionBegin;
3723   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3724   if (maxConeSize) {
3725     PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
3726   }
3727   if (maxSupportSize) {
3728     PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
3729   }
3730   PetscFunctionReturn(0);
3731 }
3732 
3733 PetscErrorCode DMSetUp_Plex(DM dm)
3734 {
3735   DM_Plex       *mesh = (DM_Plex*) dm->data;
3736   PetscInt       size, maxSupportSize;
3737 
3738   PetscFunctionBegin;
3739   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3740   PetscCall(PetscSectionSetUp(mesh->coneSection));
3741   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
3742   PetscCall(PetscMalloc1(size, &mesh->cones));
3743   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
3744   PetscCall(PetscLogObjectMemory((PetscObject) dm, size*2*sizeof(PetscInt)));
3745   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
3746   if (maxSupportSize) {
3747     PetscCall(PetscSectionSetUp(mesh->supportSection));
3748     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
3749     PetscCall(PetscMalloc1(size, &mesh->supports));
3750     PetscCall(PetscLogObjectMemory((PetscObject) dm, size*sizeof(PetscInt)));
3751   }
3752   PetscFunctionReturn(0);
3753 }
3754 
3755 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
3756 {
3757   PetscFunctionBegin;
3758   if (subdm) PetscCall(DMClone(dm, subdm));
3759   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, is, subdm));
3760   if (subdm) {(*subdm)->useNatural = dm->useNatural;}
3761   if (dm->useNatural && dm->sfMigration) {
3762     PetscSF        sfMigrationInv,sfNatural;
3763     PetscSection   section, sectionSeq;
3764 
3765     (*subdm)->sfMigration = dm->sfMigration;
3766     PetscCall(PetscObjectReference((PetscObject) dm->sfMigration));
3767     PetscCall(DMGetLocalSection((*subdm), &section));
3768     PetscCall(PetscSFCreateInverseSF((*subdm)->sfMigration, &sfMigrationInv));
3769     PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject) (*subdm)), &sectionSeq));
3770     PetscCall(PetscSFDistributeSection(sfMigrationInv, section, NULL, sectionSeq));
3771 
3772     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, sectionSeq, (*subdm)->sfMigration, &sfNatural));
3773     (*subdm)->sfNatural = sfNatural;
3774     PetscCall(PetscSectionDestroy(&sectionSeq));
3775     PetscCall(PetscSFDestroy(&sfMigrationInv));
3776   }
3777   PetscFunctionReturn(0);
3778 }
3779 
3780 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
3781 {
3782   PetscInt       i = 0;
3783 
3784   PetscFunctionBegin;
3785   PetscCall(DMClone(dms[0], superdm));
3786   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
3787   (*superdm)->useNatural = PETSC_FALSE;
3788   for (i = 0; i < len; i++) {
3789     if (dms[i]->useNatural && dms[i]->sfMigration) {
3790       PetscSF        sfMigrationInv,sfNatural;
3791       PetscSection   section, sectionSeq;
3792 
3793       (*superdm)->sfMigration = dms[i]->sfMigration;
3794       PetscCall(PetscObjectReference((PetscObject) dms[i]->sfMigration));
3795       (*superdm)->useNatural = PETSC_TRUE;
3796       PetscCall(DMGetLocalSection((*superdm), &section));
3797       PetscCall(PetscSFCreateInverseSF((*superdm)->sfMigration, &sfMigrationInv));
3798       PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject) (*superdm)), &sectionSeq));
3799       PetscCall(PetscSFDistributeSection(sfMigrationInv, section, NULL, sectionSeq));
3800 
3801       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, sectionSeq, (*superdm)->sfMigration, &sfNatural));
3802       (*superdm)->sfNatural = sfNatural;
3803       PetscCall(PetscSectionDestroy(&sectionSeq));
3804       PetscCall(PetscSFDestroy(&sfMigrationInv));
3805       break;
3806     }
3807   }
3808   PetscFunctionReturn(0);
3809 }
3810 
3811 /*@
3812   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
3813 
3814   Not collective
3815 
3816   Input Parameter:
3817 . mesh - The DMPlex
3818 
3819   Output Parameter:
3820 
3821   Note:
3822   This should be called after all calls to DMPlexSetCone()
3823 
3824   Level: beginner
3825 
3826 .seealso: `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
3827 @*/
3828 PetscErrorCode DMPlexSymmetrize(DM dm)
3829 {
3830   DM_Plex       *mesh = (DM_Plex*) dm->data;
3831   PetscInt      *offsets;
3832   PetscInt       supportSize;
3833   PetscInt       pStart, pEnd, p;
3834 
3835   PetscFunctionBegin;
3836   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3837   PetscCheck(!mesh->supports,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
3838   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize,dm,0,0,0));
3839   /* Calculate support sizes */
3840   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3841   for (p = pStart; p < pEnd; ++p) {
3842     PetscInt dof, off, c;
3843 
3844     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3845     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3846     for (c = off; c < off+dof; ++c) {
3847       PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
3848     }
3849   }
3850   PetscCall(PetscSectionSetUp(mesh->supportSection));
3851   /* Calculate supports */
3852   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
3853   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
3854   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
3855   for (p = pStart; p < pEnd; ++p) {
3856     PetscInt dof, off, c;
3857 
3858     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3859     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3860     for (c = off; c < off+dof; ++c) {
3861       const PetscInt q = mesh->cones[c];
3862       PetscInt       offS;
3863 
3864       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
3865 
3866       mesh->supports[offS+offsets[q]] = p;
3867       ++offsets[q];
3868     }
3869   }
3870   PetscCall(PetscFree(offsets));
3871   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize,dm,0,0,0));
3872   PetscFunctionReturn(0);
3873 }
3874 
3875 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
3876 {
3877   IS             stratumIS;
3878 
3879   PetscFunctionBegin;
3880   if (pStart >= pEnd) PetscFunctionReturn(0);
3881   if (PetscDefined(USE_DEBUG)) {
3882     PetscInt  qStart, qEnd, numLevels, level;
3883     PetscBool overlap = PETSC_FALSE;
3884     PetscCall(DMLabelGetNumValues(label, &numLevels));
3885     for (level = 0; level < numLevels; level++) {
3886       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
3887       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {overlap = PETSC_TRUE; break;}
3888     }
3889     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);
3890   }
3891   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd-pStart, pStart, 1, &stratumIS));
3892   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
3893   PetscCall(ISDestroy(&stratumIS));
3894   PetscFunctionReturn(0);
3895 }
3896 
3897 /*@
3898   DMPlexStratify - The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
3899   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram). The strata group all points of the
3900   same grade, and this function calculates the strata. This grade can be seen as the height (or depth) of the point in
3901   the DAG.
3902 
3903   Collective on dm
3904 
3905   Input Parameter:
3906 . mesh - The DMPlex
3907 
3908   Output Parameter:
3909 
3910   Notes:
3911   Concretely, DMPlexStratify() creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
3912   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
3913   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through DMPlexGetDepthLabel() or DMPlexGetDepthStratum(), or
3914   manually via DMGetLabel().  The height is defined implicitly by height = maxDimension - depth, and can be accessed
3915   via DMPlexGetHeightStratum().  For example, cells have height 0 and faces have height 1.
3916 
3917   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
3918   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
3919   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
3920   to interpolate only that one (e0), so that
3921 $  cone(c0) = {e0, v2}
3922 $  cone(e0) = {v0, v1}
3923   If DMPlexStratify() is run on this mesh, it will give depths
3924 $  depth 0 = {v0, v1, v2}
3925 $  depth 1 = {e0, c0}
3926   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
3927 
3928   DMPlexStratify() should be called after all calls to DMPlexSymmetrize()
3929 
3930   Level: beginner
3931 
3932 .seealso: `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
3933 @*/
3934 PetscErrorCode DMPlexStratify(DM dm)
3935 {
3936   DM_Plex       *mesh = (DM_Plex*) dm->data;
3937   DMLabel        label;
3938   PetscInt       pStart, pEnd, p;
3939   PetscInt       numRoots = 0, numLeaves = 0;
3940 
3941   PetscFunctionBegin;
3942   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3943   PetscCall(PetscLogEventBegin(DMPLEX_Stratify,dm,0,0,0));
3944 
3945   /* Create depth label */
3946   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3947   PetscCall(DMCreateLabel(dm, "depth"));
3948   PetscCall(DMPlexGetDepthLabel(dm, &label));
3949 
3950   {
3951     /* Initialize roots and count leaves */
3952     PetscInt sMin = PETSC_MAX_INT;
3953     PetscInt sMax = PETSC_MIN_INT;
3954     PetscInt coneSize, supportSize;
3955 
3956     for (p = pStart; p < pEnd; ++p) {
3957       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3958       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
3959       if (!coneSize && supportSize) {
3960         sMin = PetscMin(p, sMin);
3961         sMax = PetscMax(p, sMax);
3962         ++numRoots;
3963       } else if (!supportSize && coneSize) {
3964         ++numLeaves;
3965       } else if (!supportSize && !coneSize) {
3966         /* Isolated points */
3967         sMin = PetscMin(p, sMin);
3968         sMax = PetscMax(p, sMax);
3969       }
3970     }
3971     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax+1));
3972   }
3973 
3974   if (numRoots + numLeaves == (pEnd - pStart)) {
3975     PetscInt sMin = PETSC_MAX_INT;
3976     PetscInt sMax = PETSC_MIN_INT;
3977     PetscInt coneSize, supportSize;
3978 
3979     for (p = pStart; p < pEnd; ++p) {
3980       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3981       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
3982       if (!supportSize && coneSize) {
3983         sMin = PetscMin(p, sMin);
3984         sMax = PetscMax(p, sMax);
3985       }
3986     }
3987     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax+1));
3988   } else {
3989     PetscInt level = 0;
3990     PetscInt qStart, qEnd, q;
3991 
3992     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
3993     while (qEnd > qStart) {
3994       PetscInt sMin = PETSC_MAX_INT;
3995       PetscInt sMax = PETSC_MIN_INT;
3996 
3997       for (q = qStart; q < qEnd; ++q) {
3998         const PetscInt *support;
3999         PetscInt        supportSize, s;
4000 
4001         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4002         PetscCall(DMPlexGetSupport(dm, q, &support));
4003         for (s = 0; s < supportSize; ++s) {
4004           sMin = PetscMin(support[s], sMin);
4005           sMax = PetscMax(support[s], sMax);
4006         }
4007       }
4008       PetscCall(DMLabelGetNumValues(label, &level));
4009       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax+1));
4010       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4011     }
4012   }
4013   { /* just in case there is an empty process */
4014     PetscInt numValues, maxValues = 0, v;
4015 
4016     PetscCall(DMLabelGetNumValues(label, &numValues));
4017     PetscCallMPI(MPI_Allreduce(&numValues,&maxValues,1,MPIU_INT,MPI_MAX,PetscObjectComm((PetscObject)dm)));
4018     for (v = numValues; v < maxValues; v++) {
4019       PetscCall(DMLabelAddStratum(label, v));
4020     }
4021   }
4022   PetscCall(PetscObjectStateGet((PetscObject) label, &mesh->depthState));
4023   PetscCall(PetscLogEventEnd(DMPLEX_Stratify,dm,0,0,0));
4024   PetscFunctionReturn(0);
4025 }
4026 
4027 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4028 {
4029   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4030   PetscInt       dim, depth, pheight, coneSize;
4031 
4032   PetscFunctionBeginHot;
4033   PetscCall(DMGetDimension(dm, &dim));
4034   PetscCall(DMPlexGetDepth(dm, &depth));
4035   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4036   pheight = depth - pdepth;
4037   if (depth <= 1) {
4038     switch (pdepth) {
4039       case 0: ct = DM_POLYTOPE_POINT;break;
4040       case 1:
4041         switch (coneSize) {
4042           case 2: ct = DM_POLYTOPE_SEGMENT;break;
4043           case 3: ct = DM_POLYTOPE_TRIANGLE;break;
4044           case 4:
4045           switch (dim) {
4046             case 2: ct = DM_POLYTOPE_QUADRILATERAL;break;
4047             case 3: ct = DM_POLYTOPE_TETRAHEDRON;break;
4048             default: break;
4049           }
4050           break;
4051         case 5: ct = DM_POLYTOPE_PYRAMID;break;
4052         case 6: ct = DM_POLYTOPE_TRI_PRISM_TENSOR;break;
4053         case 8: ct = DM_POLYTOPE_HEXAHEDRON;break;
4054         default: break;
4055       }
4056     }
4057   } else {
4058     if (pdepth == 0) {
4059       ct = DM_POLYTOPE_POINT;
4060     } else if (pheight == 0) {
4061       switch (dim) {
4062         case 1:
4063           switch (coneSize) {
4064             case 2: ct = DM_POLYTOPE_SEGMENT;break;
4065             default: break;
4066           }
4067           break;
4068         case 2:
4069           switch (coneSize) {
4070             case 3: ct = DM_POLYTOPE_TRIANGLE;break;
4071             case 4: ct = DM_POLYTOPE_QUADRILATERAL;break;
4072             default: break;
4073           }
4074           break;
4075         case 3:
4076           switch (coneSize) {
4077             case 4: ct = DM_POLYTOPE_TETRAHEDRON;break;
4078             case 5:
4079             {
4080               const PetscInt *cone;
4081               PetscInt        faceConeSize;
4082 
4083               PetscCall(DMPlexGetCone(dm, p, &cone));
4084               PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4085               switch (faceConeSize) {
4086                 case 3: ct = DM_POLYTOPE_TRI_PRISM_TENSOR;break;
4087                 case 4: ct = DM_POLYTOPE_PYRAMID;break;
4088               }
4089             }
4090             break;
4091             case 6: ct = DM_POLYTOPE_HEXAHEDRON;break;
4092             default: break;
4093           }
4094           break;
4095         default: break;
4096       }
4097     } else if (pheight > 0) {
4098       switch (coneSize) {
4099         case 2: ct = DM_POLYTOPE_SEGMENT;break;
4100         case 3: ct = DM_POLYTOPE_TRIANGLE;break;
4101         case 4: ct = DM_POLYTOPE_QUADRILATERAL;break;
4102         default: break;
4103       }
4104     }
4105   }
4106   *pt = ct;
4107   PetscFunctionReturn(0);
4108 }
4109 
4110 /*@
4111   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4112 
4113   Collective on dm
4114 
4115   Input Parameter:
4116 . mesh - The DMPlex
4117 
4118   DMPlexComputeCellTypes() should be called after all calls to DMPlexSymmetrize() and DMPlexStratify()
4119 
4120   Level: developer
4121 
4122   Note: This function is normally called automatically by Plex when a cell type is requested. It creates an
4123   internal DMLabel named "celltype" which can be directly accessed using DMGetLabel(). A user may disable
4124   automatic creation by creating the label manually, using DMCreateLabel(dm, "celltype").
4125 
4126 .seealso: `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4127 @*/
4128 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4129 {
4130   DM_Plex       *mesh;
4131   DMLabel        ctLabel;
4132   PetscInt       pStart, pEnd, p;
4133 
4134   PetscFunctionBegin;
4135   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4136   mesh = (DM_Plex *) dm->data;
4137   PetscCall(DMCreateLabel(dm, "celltype"));
4138   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4139   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4140   for (p = pStart; p < pEnd; ++p) {
4141     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4142     PetscInt       pdepth;
4143 
4144     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4145     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4146     PetscCheck(ct != DM_POLYTOPE_UNKNOWN,PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " is screwed up", p);
4147     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4148   }
4149   PetscCall(PetscObjectStateGet((PetscObject) ctLabel, &mesh->celltypeState));
4150   PetscCall(PetscObjectViewFromOptions((PetscObject) ctLabel, NULL, "-dm_plex_celltypes_view"));
4151   PetscFunctionReturn(0);
4152 }
4153 
4154 /*@C
4155   DMPlexGetJoin - Get an array for the join of the set of points
4156 
4157   Not Collective
4158 
4159   Input Parameters:
4160 + dm - The DMPlex object
4161 . numPoints - The number of input points for the join
4162 - points - The input points
4163 
4164   Output Parameters:
4165 + numCoveredPoints - The number of points in the join
4166 - coveredPoints - The points in the join
4167 
4168   Level: intermediate
4169 
4170   Note: Currently, this is restricted to a single level join
4171 
4172   Fortran Notes:
4173   Since it returns an array, this routine is only available in Fortran 90, and you must
4174   include petsc.h90 in your code.
4175 
4176   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4177 
4178 .seealso: `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4179 @*/
4180 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4181 {
4182   DM_Plex       *mesh = (DM_Plex*) dm->data;
4183   PetscInt      *join[2];
4184   PetscInt       joinSize, i = 0;
4185   PetscInt       dof, off, p, c, m;
4186   PetscInt       maxSupportSize;
4187 
4188   PetscFunctionBegin;
4189   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4190   PetscValidIntPointer(points, 3);
4191   PetscValidIntPointer(numCoveredPoints, 4);
4192   PetscValidPointer(coveredPoints, 5);
4193   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4194   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4195   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4196   /* Copy in support of first point */
4197   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4198   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4199   for (joinSize = 0; joinSize < dof; ++joinSize) {
4200     join[i][joinSize] = mesh->supports[off+joinSize];
4201   }
4202   /* Check each successive support */
4203   for (p = 1; p < numPoints; ++p) {
4204     PetscInt newJoinSize = 0;
4205 
4206     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4207     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4208     for (c = 0; c < dof; ++c) {
4209       const PetscInt point = mesh->supports[off+c];
4210 
4211       for (m = 0; m < joinSize; ++m) {
4212         if (point == join[i][m]) {
4213           join[1-i][newJoinSize++] = point;
4214           break;
4215         }
4216       }
4217     }
4218     joinSize = newJoinSize;
4219     i        = 1-i;
4220   }
4221   *numCoveredPoints = joinSize;
4222   *coveredPoints    = join[i];
4223   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1-i]));
4224   PetscFunctionReturn(0);
4225 }
4226 
4227 /*@C
4228   DMPlexRestoreJoin - Restore an array for the join of the set of points
4229 
4230   Not Collective
4231 
4232   Input Parameters:
4233 + dm - The DMPlex object
4234 . numPoints - The number of input points for the join
4235 - points - The input points
4236 
4237   Output Parameters:
4238 + numCoveredPoints - The number of points in the join
4239 - coveredPoints - The points in the join
4240 
4241   Fortran Notes:
4242   Since it returns an array, this routine is only available in Fortran 90, and you must
4243   include petsc.h90 in your code.
4244 
4245   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4246 
4247   Level: intermediate
4248 
4249 .seealso: `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4250 @*/
4251 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4252 {
4253   PetscFunctionBegin;
4254   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4255   if (points) PetscValidIntPointer(points,3);
4256   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints,4);
4257   PetscValidPointer(coveredPoints, 5);
4258   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void*) coveredPoints));
4259   if (numCoveredPoints) *numCoveredPoints = 0;
4260   PetscFunctionReturn(0);
4261 }
4262 
4263 /*@C
4264   DMPlexGetFullJoin - Get an array for the join of the set of points
4265 
4266   Not Collective
4267 
4268   Input Parameters:
4269 + dm - The DMPlex object
4270 . numPoints - The number of input points for the join
4271 - points - The input points
4272 
4273   Output Parameters:
4274 + numCoveredPoints - The number of points in the join
4275 - coveredPoints - The points in the join
4276 
4277   Fortran Notes:
4278   Since it returns an array, this routine is only available in Fortran 90, and you must
4279   include petsc.h90 in your code.
4280 
4281   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4282 
4283   Level: intermediate
4284 
4285 .seealso: `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4286 @*/
4287 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4288 {
4289   PetscInt      *offsets, **closures;
4290   PetscInt      *join[2];
4291   PetscInt       depth = 0, maxSize, joinSize = 0, i = 0;
4292   PetscInt       p, d, c, m, ms;
4293 
4294   PetscFunctionBegin;
4295   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4296   PetscValidIntPointer(points, 3);
4297   PetscValidIntPointer(numCoveredPoints, 4);
4298   PetscValidPointer(coveredPoints, 5);
4299 
4300   PetscCall(DMPlexGetDepth(dm, &depth));
4301   PetscCall(PetscCalloc1(numPoints, &closures));
4302   PetscCall(DMGetWorkArray(dm, numPoints*(depth+2), MPIU_INT, &offsets));
4303   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4304   maxSize = (ms > 1) ? ((PetscPowInt(ms,depth+1)-1)/(ms-1)) : depth + 1;
4305   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4306   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4307 
4308   for (p = 0; p < numPoints; ++p) {
4309     PetscInt closureSize;
4310 
4311     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4312 
4313     offsets[p*(depth+2)+0] = 0;
4314     for (d = 0; d < depth+1; ++d) {
4315       PetscInt pStart, pEnd, i;
4316 
4317       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4318       for (i = offsets[p*(depth+2)+d]; i < closureSize; ++i) {
4319         if ((pStart > closures[p][i*2]) || (pEnd <= closures[p][i*2])) {
4320           offsets[p*(depth+2)+d+1] = i;
4321           break;
4322         }
4323       }
4324       if (i == closureSize) offsets[p*(depth+2)+d+1] = i;
4325     }
4326     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);
4327   }
4328   for (d = 0; d < depth+1; ++d) {
4329     PetscInt dof;
4330 
4331     /* Copy in support of first point */
4332     dof = offsets[d+1] - offsets[d];
4333     for (joinSize = 0; joinSize < dof; ++joinSize) {
4334       join[i][joinSize] = closures[0][(offsets[d]+joinSize)*2];
4335     }
4336     /* Check each successive cone */
4337     for (p = 1; p < numPoints && joinSize; ++p) {
4338       PetscInt newJoinSize = 0;
4339 
4340       dof = offsets[p*(depth+2)+d+1] - offsets[p*(depth+2)+d];
4341       for (c = 0; c < dof; ++c) {
4342         const PetscInt point = closures[p][(offsets[p*(depth+2)+d]+c)*2];
4343 
4344         for (m = 0; m < joinSize; ++m) {
4345           if (point == join[i][m]) {
4346             join[1-i][newJoinSize++] = point;
4347             break;
4348           }
4349         }
4350       }
4351       joinSize = newJoinSize;
4352       i        = 1-i;
4353     }
4354     if (joinSize) break;
4355   }
4356   *numCoveredPoints = joinSize;
4357   *coveredPoints    = join[i];
4358   for (p = 0; p < numPoints; ++p) {
4359     PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4360   }
4361   PetscCall(PetscFree(closures));
4362   PetscCall(DMRestoreWorkArray(dm, numPoints*(depth+2), MPIU_INT, &offsets));
4363   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1-i]));
4364   PetscFunctionReturn(0);
4365 }
4366 
4367 /*@C
4368   DMPlexGetMeet - Get an array for the meet of the set of points
4369 
4370   Not Collective
4371 
4372   Input Parameters:
4373 + dm - The DMPlex object
4374 . numPoints - The number of input points for the meet
4375 - points - The input points
4376 
4377   Output Parameters:
4378 + numCoveredPoints - The number of points in the meet
4379 - coveredPoints - The points in the meet
4380 
4381   Level: intermediate
4382 
4383   Note: Currently, this is restricted to a single level meet
4384 
4385   Fortran Notes:
4386   Since it returns an array, this routine is only available in Fortran 90, and you must
4387   include petsc.h90 in your code.
4388 
4389   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4390 
4391 .seealso: `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4392 @*/
4393 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4394 {
4395   DM_Plex       *mesh = (DM_Plex*) dm->data;
4396   PetscInt      *meet[2];
4397   PetscInt       meetSize, i = 0;
4398   PetscInt       dof, off, p, c, m;
4399   PetscInt       maxConeSize;
4400 
4401   PetscFunctionBegin;
4402   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4403   PetscValidIntPointer(points, 3);
4404   PetscValidIntPointer(numCoveringPoints, 4);
4405   PetscValidPointer(coveringPoints, 5);
4406   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4407   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4408   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4409   /* Copy in cone of first point */
4410   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4411   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4412   for (meetSize = 0; meetSize < dof; ++meetSize) {
4413     meet[i][meetSize] = mesh->cones[off+meetSize];
4414   }
4415   /* Check each successive cone */
4416   for (p = 1; p < numPoints; ++p) {
4417     PetscInt newMeetSize = 0;
4418 
4419     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4420     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4421     for (c = 0; c < dof; ++c) {
4422       const PetscInt point = mesh->cones[off+c];
4423 
4424       for (m = 0; m < meetSize; ++m) {
4425         if (point == meet[i][m]) {
4426           meet[1-i][newMeetSize++] = point;
4427           break;
4428         }
4429       }
4430     }
4431     meetSize = newMeetSize;
4432     i        = 1-i;
4433   }
4434   *numCoveringPoints = meetSize;
4435   *coveringPoints    = meet[i];
4436   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1-i]));
4437   PetscFunctionReturn(0);
4438 }
4439 
4440 /*@C
4441   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4442 
4443   Not Collective
4444 
4445   Input Parameters:
4446 + dm - The DMPlex object
4447 . numPoints - The number of input points for the meet
4448 - points - The input points
4449 
4450   Output Parameters:
4451 + numCoveredPoints - The number of points in the meet
4452 - coveredPoints - The points in the meet
4453 
4454   Level: intermediate
4455 
4456   Fortran Notes:
4457   Since it returns an array, this routine is only available in Fortran 90, and you must
4458   include petsc.h90 in your code.
4459 
4460   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4461 
4462 .seealso: `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4463 @*/
4464 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4465 {
4466   PetscFunctionBegin;
4467   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4468   if (points) PetscValidIntPointer(points,3);
4469   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints,4);
4470   PetscValidPointer(coveredPoints,5);
4471   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void*) coveredPoints));
4472   if (numCoveredPoints) *numCoveredPoints = 0;
4473   PetscFunctionReturn(0);
4474 }
4475 
4476 /*@C
4477   DMPlexGetFullMeet - Get an array for the meet of the set of points
4478 
4479   Not Collective
4480 
4481   Input Parameters:
4482 + dm - The DMPlex object
4483 . numPoints - The number of input points for the meet
4484 - points - The input points
4485 
4486   Output Parameters:
4487 + numCoveredPoints - The number of points in the meet
4488 - coveredPoints - The points in the meet
4489 
4490   Level: intermediate
4491 
4492   Fortran Notes:
4493   Since it returns an array, this routine is only available in Fortran 90, and you must
4494   include petsc.h90 in your code.
4495 
4496   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4497 
4498 .seealso: `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4499 @*/
4500 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4501 {
4502   PetscInt      *offsets, **closures;
4503   PetscInt      *meet[2];
4504   PetscInt       height = 0, maxSize, meetSize = 0, i = 0;
4505   PetscInt       p, h, c, m, mc;
4506 
4507   PetscFunctionBegin;
4508   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4509   PetscValidIntPointer(points, 3);
4510   PetscValidIntPointer(numCoveredPoints, 4);
4511   PetscValidPointer(coveredPoints, 5);
4512 
4513   PetscCall(DMPlexGetDepth(dm, &height));
4514   PetscCall(PetscMalloc1(numPoints, &closures));
4515   PetscCall(DMGetWorkArray(dm, numPoints*(height+2), MPIU_INT, &offsets));
4516   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4517   maxSize = (mc > 1) ? ((PetscPowInt(mc,height+1)-1)/(mc-1)) : height + 1;
4518   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4519   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4520 
4521   for (p = 0; p < numPoints; ++p) {
4522     PetscInt closureSize;
4523 
4524     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4525 
4526     offsets[p*(height+2)+0] = 0;
4527     for (h = 0; h < height+1; ++h) {
4528       PetscInt pStart, pEnd, i;
4529 
4530       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4531       for (i = offsets[p*(height+2)+h]; i < closureSize; ++i) {
4532         if ((pStart > closures[p][i*2]) || (pEnd <= closures[p][i*2])) {
4533           offsets[p*(height+2)+h+1] = i;
4534           break;
4535         }
4536       }
4537       if (i == closureSize) offsets[p*(height+2)+h+1] = i;
4538     }
4539     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);
4540   }
4541   for (h = 0; h < height+1; ++h) {
4542     PetscInt dof;
4543 
4544     /* Copy in cone of first point */
4545     dof = offsets[h+1] - offsets[h];
4546     for (meetSize = 0; meetSize < dof; ++meetSize) {
4547       meet[i][meetSize] = closures[0][(offsets[h]+meetSize)*2];
4548     }
4549     /* Check each successive cone */
4550     for (p = 1; p < numPoints && meetSize; ++p) {
4551       PetscInt newMeetSize = 0;
4552 
4553       dof = offsets[p*(height+2)+h+1] - offsets[p*(height+2)+h];
4554       for (c = 0; c < dof; ++c) {
4555         const PetscInt point = closures[p][(offsets[p*(height+2)+h]+c)*2];
4556 
4557         for (m = 0; m < meetSize; ++m) {
4558           if (point == meet[i][m]) {
4559             meet[1-i][newMeetSize++] = point;
4560             break;
4561           }
4562         }
4563       }
4564       meetSize = newMeetSize;
4565       i        = 1-i;
4566     }
4567     if (meetSize) break;
4568   }
4569   *numCoveredPoints = meetSize;
4570   *coveredPoints    = meet[i];
4571   for (p = 0; p < numPoints; ++p) {
4572     PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
4573   }
4574   PetscCall(PetscFree(closures));
4575   PetscCall(DMRestoreWorkArray(dm, numPoints*(height+2), MPIU_INT, &offsets));
4576   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1-i]));
4577   PetscFunctionReturn(0);
4578 }
4579 
4580 /*@C
4581   DMPlexEqual - Determine if two DMs have the same topology
4582 
4583   Not Collective
4584 
4585   Input Parameters:
4586 + dmA - A DMPlex object
4587 - dmB - A DMPlex object
4588 
4589   Output Parameters:
4590 . equal - PETSC_TRUE if the topologies are identical
4591 
4592   Level: intermediate
4593 
4594   Notes:
4595   We are not solving graph isomorphism, so we do not permutation.
4596 
4597 .seealso: `DMPlexGetCone()`
4598 @*/
4599 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
4600 {
4601   PetscInt       depth, depthB, pStart, pEnd, pStartB, pEndB, p;
4602 
4603   PetscFunctionBegin;
4604   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
4605   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
4606   PetscValidBoolPointer(equal, 3);
4607 
4608   *equal = PETSC_FALSE;
4609   PetscCall(DMPlexGetDepth(dmA, &depth));
4610   PetscCall(DMPlexGetDepth(dmB, &depthB));
4611   if (depth != depthB) PetscFunctionReturn(0);
4612   PetscCall(DMPlexGetChart(dmA, &pStart,  &pEnd));
4613   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
4614   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(0);
4615   for (p = pStart; p < pEnd; ++p) {
4616     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
4617     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
4618 
4619     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
4620     PetscCall(DMPlexGetCone(dmA, p, &cone));
4621     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
4622     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
4623     PetscCall(DMPlexGetCone(dmB, p, &coneB));
4624     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
4625     if (coneSize != coneSizeB) PetscFunctionReturn(0);
4626     for (c = 0; c < coneSize; ++c) {
4627       if (cone[c] != coneB[c]) PetscFunctionReturn(0);
4628       if (ornt[c] != orntB[c]) PetscFunctionReturn(0);
4629     }
4630     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
4631     PetscCall(DMPlexGetSupport(dmA, p, &support));
4632     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
4633     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
4634     if (supportSize != supportSizeB) PetscFunctionReturn(0);
4635     for (s = 0; s < supportSize; ++s) {
4636       if (support[s] != supportB[s]) PetscFunctionReturn(0);
4637     }
4638   }
4639   *equal = PETSC_TRUE;
4640   PetscFunctionReturn(0);
4641 }
4642 
4643 /*@C
4644   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
4645 
4646   Not Collective
4647 
4648   Input Parameters:
4649 + dm         - The DMPlex
4650 . cellDim    - The cell dimension
4651 - numCorners - The number of vertices on a cell
4652 
4653   Output Parameters:
4654 . numFaceVertices - The number of vertices on a face
4655 
4656   Level: developer
4657 
4658   Notes:
4659   Of course this can only work for a restricted set of symmetric shapes
4660 
4661 .seealso: `DMPlexGetCone()`
4662 @*/
4663 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
4664 {
4665   MPI_Comm       comm;
4666 
4667   PetscFunctionBegin;
4668   PetscCall(PetscObjectGetComm((PetscObject)dm,&comm));
4669   PetscValidIntPointer(numFaceVertices,4);
4670   switch (cellDim) {
4671   case 0:
4672     *numFaceVertices = 0;
4673     break;
4674   case 1:
4675     *numFaceVertices = 1;
4676     break;
4677   case 2:
4678     switch (numCorners) {
4679     case 3: /* triangle */
4680       *numFaceVertices = 2; /* Edge has 2 vertices */
4681       break;
4682     case 4: /* quadrilateral */
4683       *numFaceVertices = 2; /* Edge has 2 vertices */
4684       break;
4685     case 6: /* quadratic triangle, tri and quad cohesive Lagrange cells */
4686       *numFaceVertices = 3; /* Edge has 3 vertices */
4687       break;
4688     case 9: /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
4689       *numFaceVertices = 3; /* Edge has 3 vertices */
4690       break;
4691     default:
4692       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4693     }
4694     break;
4695   case 3:
4696     switch (numCorners) {
4697     case 4: /* tetradehdron */
4698       *numFaceVertices = 3; /* Face has 3 vertices */
4699       break;
4700     case 6: /* tet cohesive cells */
4701       *numFaceVertices = 4; /* Face has 4 vertices */
4702       break;
4703     case 8: /* hexahedron */
4704       *numFaceVertices = 4; /* Face has 4 vertices */
4705       break;
4706     case 9: /* tet cohesive Lagrange cells */
4707       *numFaceVertices = 6; /* Face has 6 vertices */
4708       break;
4709     case 10: /* quadratic tetrahedron */
4710       *numFaceVertices = 6; /* Face has 6 vertices */
4711       break;
4712     case 12: /* hex cohesive Lagrange cells */
4713       *numFaceVertices = 6; /* Face has 6 vertices */
4714       break;
4715     case 18: /* quadratic tet cohesive Lagrange cells */
4716       *numFaceVertices = 6; /* Face has 6 vertices */
4717       break;
4718     case 27: /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
4719       *numFaceVertices = 9; /* Face has 9 vertices */
4720       break;
4721     default:
4722       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4723     }
4724     break;
4725   default:
4726     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
4727   }
4728   PetscFunctionReturn(0);
4729 }
4730 
4731 /*@
4732   DMPlexGetDepthLabel - Get the DMLabel recording the depth of each point
4733 
4734   Not Collective
4735 
4736   Input Parameter:
4737 . dm    - The DMPlex object
4738 
4739   Output Parameter:
4740 . depthLabel - The DMLabel recording point depth
4741 
4742   Level: developer
4743 
4744 .seealso: `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
4745 @*/
4746 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
4747 {
4748   PetscFunctionBegin;
4749   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4750   PetscValidPointer(depthLabel, 2);
4751   *depthLabel = dm->depthLabel;
4752   PetscFunctionReturn(0);
4753 }
4754 
4755 /*@
4756   DMPlexGetDepth - Get the depth of the DAG representing this mesh
4757 
4758   Not Collective
4759 
4760   Input Parameter:
4761 . dm    - The DMPlex object
4762 
4763   Output Parameter:
4764 . depth - The number of strata (breadth first levels) in the DAG
4765 
4766   Level: developer
4767 
4768   Notes:
4769   This returns maximum of point depths over all points, i.e. maximum value of the label returned by DMPlexGetDepthLabel().
4770   The point depth is described more in detail in DMPlexGetDepthStratum().
4771   An empty mesh gives -1.
4772 
4773 .seealso: `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
4774 @*/
4775 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
4776 {
4777   DMLabel        label;
4778   PetscInt       d = 0;
4779 
4780   PetscFunctionBegin;
4781   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4782   PetscValidIntPointer(depth, 2);
4783   PetscCall(DMPlexGetDepthLabel(dm, &label));
4784   if (label) PetscCall(DMLabelGetNumValues(label, &d));
4785   *depth = d-1;
4786   PetscFunctionReturn(0);
4787 }
4788 
4789 /*@
4790   DMPlexGetDepthStratum - Get the bounds [start, end) for all points at a certain depth.
4791 
4792   Not Collective
4793 
4794   Input Parameters:
4795 + dm    - The DMPlex object
4796 - depth - The requested depth
4797 
4798   Output Parameters:
4799 + start - The first point at this depth
4800 - end   - One beyond the last point at this depth
4801 
4802   Notes:
4803   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
4804   often "vertices".  If the mesh is "interpolated" (see DMPlexInterpolate()), then depth stratum 1 contains the next
4805   higher dimension, e.g., "edges".
4806 
4807   Level: developer
4808 
4809 .seealso: `DMPlexGetHeightStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
4810 @*/
4811 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
4812 {
4813   DMLabel        label;
4814   PetscInt       pStart, pEnd;
4815 
4816   PetscFunctionBegin;
4817   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4818   if (start) {PetscValidIntPointer(start, 3); *start = 0;}
4819   if (end)   {PetscValidIntPointer(end,   4); *end   = 0;}
4820   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4821   if (pStart == pEnd) PetscFunctionReturn(0);
4822   if (depth < 0) {
4823     if (start) *start = pStart;
4824     if (end)   *end   = pEnd;
4825     PetscFunctionReturn(0);
4826   }
4827   PetscCall(DMPlexGetDepthLabel(dm, &label));
4828   PetscCheck(label,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
4829   PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
4830   PetscFunctionReturn(0);
4831 }
4832 
4833 /*@
4834   DMPlexGetHeightStratum - Get the bounds [start, end) for all points at a certain height.
4835 
4836   Not Collective
4837 
4838   Input Parameters:
4839 + dm     - The DMPlex object
4840 - height - The requested height
4841 
4842   Output Parameters:
4843 + start - The first point at this height
4844 - end   - One beyond the last point at this height
4845 
4846   Notes:
4847   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
4848   points, often called "cells" or "elements".  If the mesh is "interpolated" (see DMPlexInterpolate()), then height
4849   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
4850 
4851   Level: developer
4852 
4853 .seealso: `DMPlexGetDepthStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
4854 @*/
4855 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
4856 {
4857   DMLabel        label;
4858   PetscInt       depth, pStart, pEnd;
4859 
4860   PetscFunctionBegin;
4861   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4862   if (start) {PetscValidIntPointer(start, 3); *start = 0;}
4863   if (end)   {PetscValidIntPointer(end,   4); *end   = 0;}
4864   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4865   if (pStart == pEnd) PetscFunctionReturn(0);
4866   if (height < 0) {
4867     if (start) *start = pStart;
4868     if (end)   *end   = pEnd;
4869     PetscFunctionReturn(0);
4870   }
4871   PetscCall(DMPlexGetDepthLabel(dm, &label));
4872   PetscCheck(label,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
4873   PetscCall(DMLabelGetNumValues(label, &depth));
4874   PetscCall(DMLabelGetStratumBounds(label, depth-1-height, start, end));
4875   PetscFunctionReturn(0);
4876 }
4877 
4878 /*@
4879   DMPlexGetPointDepth - Get the depth of a given point
4880 
4881   Not Collective
4882 
4883   Input Parameters:
4884 + dm    - The DMPlex object
4885 - point - The point
4886 
4887   Output Parameter:
4888 . depth - The depth of the point
4889 
4890   Level: intermediate
4891 
4892 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
4893 @*/
4894 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
4895 {
4896   PetscFunctionBegin;
4897   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4898   PetscValidIntPointer(depth, 3);
4899   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
4900   PetscFunctionReturn(0);
4901 }
4902 
4903 /*@
4904   DMPlexGetPointHeight - Get the height of a given point
4905 
4906   Not Collective
4907 
4908   Input Parameters:
4909 + dm    - The DMPlex object
4910 - point - The point
4911 
4912   Output Parameter:
4913 . height - The height of the point
4914 
4915   Level: intermediate
4916 
4917 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
4918 @*/
4919 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
4920 {
4921   PetscInt       n, pDepth;
4922 
4923   PetscFunctionBegin;
4924   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4925   PetscValidIntPointer(height, 3);
4926   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
4927   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
4928   *height = n - 1 - pDepth;  /* DAG depth is n-1 */
4929   PetscFunctionReturn(0);
4930 }
4931 
4932 /*@
4933   DMPlexGetCellTypeLabel - Get the DMLabel recording the polytope type of each cell
4934 
4935   Not Collective
4936 
4937   Input Parameter:
4938 . dm - The DMPlex object
4939 
4940   Output Parameter:
4941 . celltypeLabel - The DMLabel recording cell polytope type
4942 
4943   Note: This function will trigger automatica computation of cell types. This can be disabled by calling
4944   DMCreateLabel(dm, "celltype") beforehand.
4945 
4946   Level: developer
4947 
4948 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
4949 @*/
4950 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
4951 {
4952   PetscFunctionBegin;
4953   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4954   PetscValidPointer(celltypeLabel, 2);
4955   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
4956   *celltypeLabel = dm->celltypeLabel;
4957   PetscFunctionReturn(0);
4958 }
4959 
4960 /*@
4961   DMPlexGetCellType - Get the polytope type of a given cell
4962 
4963   Not Collective
4964 
4965   Input Parameters:
4966 + dm   - The DMPlex object
4967 - cell - The cell
4968 
4969   Output Parameter:
4970 . celltype - The polytope type of the cell
4971 
4972   Level: intermediate
4973 
4974 .seealso: `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
4975 @*/
4976 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
4977 {
4978   DMLabel        label;
4979   PetscInt       ct;
4980 
4981   PetscFunctionBegin;
4982   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4983   PetscValidPointer(celltype, 3);
4984   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
4985   PetscCall(DMLabelGetValue(label, cell, &ct));
4986   PetscCheck(ct >= 0,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
4987   *celltype = (DMPolytopeType) ct;
4988   PetscFunctionReturn(0);
4989 }
4990 
4991 /*@
4992   DMPlexSetCellType - Set the polytope type of a given cell
4993 
4994   Not Collective
4995 
4996   Input Parameters:
4997 + dm   - The DMPlex object
4998 . cell - The cell
4999 - celltype - The polytope type of the cell
5000 
5001   Note: By default, cell types will be automatically computed using DMPlexComputeCellTypes() before this function
5002   is executed. This function will override the computed type. However, if automatic classification will not succeed
5003   and a user wants to manually specify all types, the classification must be disabled by calling
5004   DMCreaateLabel(dm, "celltype") before getting or setting any cell types.
5005 
5006   Level: advanced
5007 
5008 .seealso: `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5009 @*/
5010 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5011 {
5012   DMLabel        label;
5013 
5014   PetscFunctionBegin;
5015   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5016   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5017   PetscCall(DMLabelSetValue(label, cell, celltype));
5018   PetscFunctionReturn(0);
5019 }
5020 
5021 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5022 {
5023   PetscSection   section, s;
5024   Mat            m;
5025   PetscInt       maxHeight;
5026 
5027   PetscFunctionBegin;
5028   PetscCall(DMClone(dm, cdm));
5029   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5030   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5031   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5032   PetscCall(DMSetLocalSection(*cdm, section));
5033   PetscCall(PetscSectionDestroy(&section));
5034   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
5035   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
5036   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
5037   PetscCall(PetscSectionDestroy(&s));
5038   PetscCall(MatDestroy(&m));
5039 
5040   PetscCall(DMSetNumFields(*cdm, 1));
5041   PetscCall(DMCreateDS(*cdm));
5042   PetscFunctionReturn(0);
5043 }
5044 
5045 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5046 {
5047   Vec            coordsLocal;
5048   DM             coordsDM;
5049 
5050   PetscFunctionBegin;
5051   *field = NULL;
5052   PetscCall(DMGetCoordinatesLocal(dm,&coordsLocal));
5053   PetscCall(DMGetCoordinateDM(dm,&coordsDM));
5054   if (coordsLocal && coordsDM) {
5055     PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5056   }
5057   PetscFunctionReturn(0);
5058 }
5059 
5060 /*@C
5061   DMPlexGetConeSection - Return a section which describes the layout of cone data
5062 
5063   Not Collective
5064 
5065   Input Parameters:
5066 . dm        - The DMPlex object
5067 
5068   Output Parameter:
5069 . section - The PetscSection object
5070 
5071   Level: developer
5072 
5073 .seealso: `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`
5074 @*/
5075 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5076 {
5077   DM_Plex *mesh = (DM_Plex*) dm->data;
5078 
5079   PetscFunctionBegin;
5080   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5081   if (section) *section = mesh->coneSection;
5082   PetscFunctionReturn(0);
5083 }
5084 
5085 /*@C
5086   DMPlexGetSupportSection - Return a section which describes the layout of support data
5087 
5088   Not Collective
5089 
5090   Input Parameters:
5091 . dm        - The DMPlex object
5092 
5093   Output Parameter:
5094 . section - The PetscSection object
5095 
5096   Level: developer
5097 
5098 .seealso: `DMPlexGetConeSection()`
5099 @*/
5100 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5101 {
5102   DM_Plex *mesh = (DM_Plex*) dm->data;
5103 
5104   PetscFunctionBegin;
5105   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5106   if (section) *section = mesh->supportSection;
5107   PetscFunctionReturn(0);
5108 }
5109 
5110 /*@C
5111   DMPlexGetCones - Return cone data
5112 
5113   Not Collective
5114 
5115   Input Parameters:
5116 . dm        - The DMPlex object
5117 
5118   Output Parameter:
5119 . cones - The cone for each point
5120 
5121   Level: developer
5122 
5123 .seealso: `DMPlexGetConeSection()`
5124 @*/
5125 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5126 {
5127   DM_Plex *mesh = (DM_Plex*) dm->data;
5128 
5129   PetscFunctionBegin;
5130   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5131   if (cones) *cones = mesh->cones;
5132   PetscFunctionReturn(0);
5133 }
5134 
5135 /*@C
5136   DMPlexGetConeOrientations - Return cone orientation data
5137 
5138   Not Collective
5139 
5140   Input Parameters:
5141 . dm        - The DMPlex object
5142 
5143   Output Parameter:
5144 . coneOrientations - The array of cone orientations for all points
5145 
5146   Level: developer
5147 
5148   Notes:
5149   The PetscSection returned by DMPlexGetConeSection() partitions coneOrientations into cone orientations of particular points as returned by DMPlexGetConeOrientation().
5150 
5151   The meaning of coneOrientations values is detailed in DMPlexGetConeOrientation().
5152 
5153 .seealso: `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`
5154 @*/
5155 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5156 {
5157   DM_Plex *mesh = (DM_Plex*) dm->data;
5158 
5159   PetscFunctionBegin;
5160   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5161   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5162   PetscFunctionReturn(0);
5163 }
5164 
5165 /******************************** FEM Support **********************************/
5166 
5167 /*
5168  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5169  representing a line in the section.
5170 */
5171 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(PetscSection section,PetscInt field,PetscInt line,PetscBool vertexchart,PetscInt *Nc,PetscInt *k)
5172 {
5173   PetscFunctionBeginHot;
5174   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5175   if (line < 0) {
5176     *k = 0;
5177     *Nc = 0;
5178   } else if (vertexchart) {            /* If we only have a vertex chart, we must have degree k=1 */
5179     *k = 1;
5180   } else {                      /* Assume the full interpolated mesh is in the chart; lines in particular */
5181     /* An order k SEM disc has k-1 dofs on an edge */
5182     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5183     *k = *k / *Nc + 1;
5184   }
5185   PetscFunctionReturn(0);
5186 }
5187 
5188 /*@
5189 
5190   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5191   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5192   section provided (or the section of the DM).
5193 
5194   Input Parameters:
5195 + dm      - The DM
5196 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or PETSC_DETERMINE
5197 - section - The PetscSection to reorder, or NULL for the default section
5198 
5199   Note: The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5200   degree of the basis.
5201 
5202   Example:
5203   A typical interpolated single-quad mesh might order points as
5204 .vb
5205   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5206 
5207   v4 -- e6 -- v3
5208   |           |
5209   e7    c0    e8
5210   |           |
5211   v1 -- e5 -- v2
5212 .ve
5213 
5214   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5215   dofs in the order of points, e.g.,
5216 .vb
5217     c0 -> [0,1,2,3]
5218     v1 -> [4]
5219     ...
5220     e5 -> [8, 9]
5221 .ve
5222 
5223   which corresponds to the dofs
5224 .vb
5225     6   10  11  7
5226     13  2   3   15
5227     12  0   1   14
5228     4   8   9   5
5229 .ve
5230 
5231   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5232 .vb
5233   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5234 .ve
5235 
5236   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5237 .vb
5238    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5239 .ve
5240 
5241   Level: developer
5242 
5243 .seealso: `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5244 @*/
5245 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5246 {
5247   DMLabel        label;
5248   PetscInt       dim, depth = -1, eStart = -1, Nf;
5249   PetscBool      vertexchart;
5250 
5251   PetscFunctionBegin;
5252   PetscCall(DMGetDimension(dm, &dim));
5253   if (dim < 1) PetscFunctionReturn(0);
5254   if (point < 0) {
5255     PetscInt sStart,sEnd;
5256 
5257     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5258     point = sEnd-sStart ? sStart : point;
5259   }
5260   PetscCall(DMPlexGetDepthLabel(dm, &label));
5261   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5262   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5263   if (depth == 1) {eStart = point;}
5264   else if  (depth == dim) {
5265     const PetscInt *cone;
5266 
5267     PetscCall(DMPlexGetCone(dm, point, &cone));
5268     if (dim == 2) eStart = cone[0];
5269     else if (dim == 3) {
5270       const PetscInt *cone2;
5271       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5272       eStart = cone2[0];
5273     } 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);
5274   } 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);
5275   {                             /* Determine whether the chart covers all points or just vertices. */
5276     PetscInt pStart,pEnd,cStart,cEnd;
5277     PetscCall(DMPlexGetDepthStratum(dm,0,&pStart,&pEnd));
5278     PetscCall(PetscSectionGetChart(section,&cStart,&cEnd));
5279     if (pStart == cStart && pEnd == cEnd) vertexchart = PETSC_TRUE;      /* Only vertices are in the chart */
5280     else if (cStart <= point && point < cEnd) vertexchart = PETSC_FALSE; /* Some interpolated points exist in the chart */
5281     else vertexchart = PETSC_TRUE;                                       /* Some interpolated points are not in chart; assume dofs only at cells and vertices */
5282   }
5283   PetscCall(PetscSectionGetNumFields(section, &Nf));
5284   for (PetscInt d=1; d<=dim; d++) {
5285     PetscInt k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5286     PetscInt *perm;
5287 
5288     for (f = 0; f < Nf; ++f) {
5289       PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5290       size += PetscPowInt(k+1, d)*Nc;
5291     }
5292     PetscCall(PetscMalloc1(size, &perm));
5293     for (f = 0; f < Nf; ++f) {
5294       switch (d) {
5295       case 1:
5296         PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5297         /*
5298          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5299          We want              [ vtx0; edge of length k-1; vtx1 ]
5300          */
5301         for (c=0; c<Nc; c++,offset++) perm[offset] = (k-1)*Nc + c + foffset;
5302         for (i=0; i<k-1; i++) for (c=0; c<Nc; c++,offset++) perm[offset] = i*Nc + c + foffset;
5303         for (c=0; c<Nc; c++,offset++) perm[offset] = k*Nc + c + foffset;
5304         foffset = offset;
5305         break;
5306       case 2:
5307         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5308         PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5309         /* The SEM order is
5310 
5311          v_lb, {e_b}, v_rb,
5312          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5313          v_lt, reverse {e_t}, v_rt
5314          */
5315         {
5316           const PetscInt of   = 0;
5317           const PetscInt oeb  = of   + PetscSqr(k-1);
5318           const PetscInt oer  = oeb  + (k-1);
5319           const PetscInt oet  = oer  + (k-1);
5320           const PetscInt oel  = oet  + (k-1);
5321           const PetscInt ovlb = oel  + (k-1);
5322           const PetscInt ovrb = ovlb + 1;
5323           const PetscInt ovrt = ovrb + 1;
5324           const PetscInt ovlt = ovrt + 1;
5325           PetscInt       o;
5326 
5327           /* bottom */
5328           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb*Nc + c + foffset;
5329           for (o = oeb; o < oer; ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5330           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb*Nc + c + foffset;
5331           /* middle */
5332           for (i = 0; i < k-1; ++i) {
5333             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel+(k-2)-i)*Nc + c + foffset;
5334             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;
5335             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer+i)*Nc + c + foffset;
5336           }
5337           /* top */
5338           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt*Nc + c + foffset;
5339           for (o = oel-1; o >= oet; --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5340           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt*Nc + c + foffset;
5341           foffset = offset;
5342         }
5343         break;
5344       case 3:
5345         /* The original hex closure is
5346 
5347          {c,
5348          f_b, f_t, f_f, f_b, f_r, f_l,
5349          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5350          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5351          */
5352         PetscCall(PetscSectionFieldGetTensorDegree_Private(section,f,eStart,vertexchart,&Nc,&k));
5353         /* The SEM order is
5354          Bottom Slice
5355          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5356          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5357          v_blb, {e_bb}, v_brb,
5358 
5359          Middle Slice (j)
5360          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5361          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5362          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5363 
5364          Top Slice
5365          v_tlf, {e_tf}, v_trf,
5366          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5367          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5368          */
5369         {
5370           const PetscInt oc    = 0;
5371           const PetscInt ofb   = oc    + PetscSqr(k-1)*(k-1);
5372           const PetscInt oft   = ofb   + PetscSqr(k-1);
5373           const PetscInt off   = oft   + PetscSqr(k-1);
5374           const PetscInt ofk   = off   + PetscSqr(k-1);
5375           const PetscInt ofr   = ofk   + PetscSqr(k-1);
5376           const PetscInt ofl   = ofr   + PetscSqr(k-1);
5377           const PetscInt oebl  = ofl   + PetscSqr(k-1);
5378           const PetscInt oebb  = oebl  + (k-1);
5379           const PetscInt oebr  = oebb  + (k-1);
5380           const PetscInt oebf  = oebr  + (k-1);
5381           const PetscInt oetf  = oebf  + (k-1);
5382           const PetscInt oetr  = oetf  + (k-1);
5383           const PetscInt oetb  = oetr  + (k-1);
5384           const PetscInt oetl  = oetb  + (k-1);
5385           const PetscInt oerf  = oetl  + (k-1);
5386           const PetscInt oelf  = oerf  + (k-1);
5387           const PetscInt oelb  = oelf  + (k-1);
5388           const PetscInt oerb  = oelb  + (k-1);
5389           const PetscInt ovblf = oerb  + (k-1);
5390           const PetscInt ovblb = ovblf + 1;
5391           const PetscInt ovbrb = ovblb + 1;
5392           const PetscInt ovbrf = ovbrb + 1;
5393           const PetscInt ovtlf = ovbrf + 1;
5394           const PetscInt ovtrf = ovtlf + 1;
5395           const PetscInt ovtrb = ovtrf + 1;
5396           const PetscInt ovtlb = ovtrb + 1;
5397           PetscInt       o, n;
5398 
5399           /* Bottom Slice */
5400           /*   bottom */
5401           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf*Nc + c + foffset;
5402           for (o = oetf-1; o >= oebf; --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5403           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf*Nc + c + foffset;
5404           /*   middle */
5405           for (i = 0; i < k-1; ++i) {
5406             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl+i)*Nc + c + foffset;
5407             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;}
5408             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr+(k-2)-i)*Nc + c + foffset;
5409           }
5410           /*   top */
5411           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb*Nc + c + foffset;
5412           for (o = oebb; o < oebr; ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5413           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb*Nc + c + foffset;
5414 
5415           /* Middle Slice */
5416           for (j = 0; j < k-1; ++j) {
5417             /*   bottom */
5418             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf+(k-2)-j)*Nc + c + foffset;
5419             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;
5420             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf+j)*Nc + c + foffset;
5421             /*   middle */
5422             for (i = 0; i < k-1; ++i) {
5423               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl+i*(k-1)+j)*Nc + c + foffset;
5424               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;
5425               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr+j*(k-1)+i)*Nc + c + foffset;
5426             }
5427             /*   top */
5428             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb+j)*Nc + c + foffset;
5429             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;
5430             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb+(k-2)-j)*Nc + c + foffset;
5431           }
5432 
5433           /* Top Slice */
5434           /*   bottom */
5435           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf*Nc + c + foffset;
5436           for (o = oetf; o < oetr; ++o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5437           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf*Nc + c + foffset;
5438           /*   middle */
5439           for (i = 0; i < k-1; ++i) {
5440             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl+(k-2)-i)*Nc + c + foffset;
5441             for (n = 0; n < k-1; ++n) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft+i*(k-1)+n)*Nc + c + foffset;
5442             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr+i)*Nc + c + foffset;
5443           }
5444           /*   top */
5445           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb*Nc + c + foffset;
5446           for (o = oetl-1; o >= oetb; --o) for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o*Nc + c + foffset;
5447           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb*Nc + c + foffset;
5448 
5449           foffset = offset;
5450         }
5451         break;
5452       default: SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
5453       }
5454     }
5455     PetscCheck(offset == size,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
5456     /* Check permutation */
5457     {
5458       PetscInt *check;
5459 
5460       PetscCall(PetscMalloc1(size, &check));
5461       for (i = 0; i < size; ++i) {
5462         check[i] = -1;
5463         PetscCheck(perm[i] >= 0 && perm[i] < size,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
5464       }
5465       for (i = 0; i < size; ++i) check[perm[i]] = i;
5466       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
5467       PetscCall(PetscFree(check));
5468     }
5469     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject) dm, d, size, PETSC_OWN_POINTER, perm));
5470     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
5471       PetscInt *loc_perm;
5472       PetscCall(PetscMalloc1(size*2, &loc_perm));
5473       for (PetscInt i=0; i<size; i++) {
5474         loc_perm[i] = perm[i];
5475         loc_perm[size+i] = size + perm[i];
5476       }
5477       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject) dm, d, size*2, PETSC_OWN_POINTER, loc_perm));
5478     }
5479   }
5480   PetscFunctionReturn(0);
5481 }
5482 
5483 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
5484 {
5485   PetscDS        prob;
5486   PetscInt       depth, Nf, h;
5487   DMLabel        label;
5488 
5489   PetscFunctionBeginHot;
5490   PetscCall(DMGetDS(dm, &prob));
5491   Nf      = prob->Nf;
5492   label   = dm->depthLabel;
5493   *dspace = NULL;
5494   if (field < Nf) {
5495     PetscObject disc = prob->disc[field];
5496 
5497     if (disc->classid == PETSCFE_CLASSID) {
5498       PetscDualSpace dsp;
5499 
5500       PetscCall(PetscFEGetDualSpace((PetscFE)disc,&dsp));
5501       PetscCall(DMLabelGetNumValues(label,&depth));
5502       PetscCall(DMLabelGetValue(label,point,&h));
5503       h    = depth - 1 - h;
5504       if (h) {
5505         PetscCall(PetscDualSpaceGetHeightSubspace(dsp,h,dspace));
5506       } else {
5507         *dspace = dsp;
5508       }
5509     }
5510   }
5511   PetscFunctionReturn(0);
5512 }
5513 
5514 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5515 {
5516   PetscScalar    *array, *vArray;
5517   const PetscInt *cone, *coneO;
5518   PetscInt        pStart, pEnd, p, numPoints, size = 0, offset = 0;
5519 
5520   PetscFunctionBeginHot;
5521   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5522   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
5523   PetscCall(DMPlexGetCone(dm, point, &cone));
5524   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
5525   if (!values || !*values) {
5526     if ((point >= pStart) && (point < pEnd)) {
5527       PetscInt dof;
5528 
5529       PetscCall(PetscSectionGetDof(section, point, &dof));
5530       size += dof;
5531     }
5532     for (p = 0; p < numPoints; ++p) {
5533       const PetscInt cp = cone[p];
5534       PetscInt       dof;
5535 
5536       if ((cp < pStart) || (cp >= pEnd)) continue;
5537       PetscCall(PetscSectionGetDof(section, cp, &dof));
5538       size += dof;
5539     }
5540     if (!values) {
5541       if (csize) *csize = size;
5542       PetscFunctionReturn(0);
5543     }
5544     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
5545   } else {
5546     array = *values;
5547   }
5548   size = 0;
5549   PetscCall(VecGetArray(v, &vArray));
5550   if ((point >= pStart) && (point < pEnd)) {
5551     PetscInt     dof, off, d;
5552     PetscScalar *varr;
5553 
5554     PetscCall(PetscSectionGetDof(section, point, &dof));
5555     PetscCall(PetscSectionGetOffset(section, point, &off));
5556     varr = &vArray[off];
5557     for (d = 0; d < dof; ++d, ++offset) {
5558       array[offset] = varr[d];
5559     }
5560     size += dof;
5561   }
5562   for (p = 0; p < numPoints; ++p) {
5563     const PetscInt cp = cone[p];
5564     PetscInt       o  = coneO[p];
5565     PetscInt       dof, off, d;
5566     PetscScalar   *varr;
5567 
5568     if ((cp < pStart) || (cp >= pEnd)) continue;
5569     PetscCall(PetscSectionGetDof(section, cp, &dof));
5570     PetscCall(PetscSectionGetOffset(section, cp, &off));
5571     varr = &vArray[off];
5572     if (o >= 0) {
5573       for (d = 0; d < dof; ++d, ++offset) {
5574         array[offset] = varr[d];
5575       }
5576     } else {
5577       for (d = dof-1; d >= 0; --d, ++offset) {
5578         array[offset] = varr[d];
5579       }
5580     }
5581     size += dof;
5582   }
5583   PetscCall(VecRestoreArray(v, &vArray));
5584   if (!*values) {
5585     if (csize) *csize = size;
5586     *values = array;
5587   } else {
5588     PetscCheck(size <= *csize,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5589     *csize = size;
5590   }
5591   PetscFunctionReturn(0);
5592 }
5593 
5594 /* Compress out points not in the section */
5595 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
5596 {
5597   const PetscInt np = *numPoints;
5598   PetscInt       pStart, pEnd, p, q;
5599 
5600   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5601   for (p = 0, q = 0; p < np; ++p) {
5602     const PetscInt r = points[p*2];
5603     if ((r >= pStart) && (r < pEnd)) {
5604       points[q*2]   = r;
5605       points[q*2+1] = points[p*2+1];
5606       ++q;
5607     }
5608   }
5609   *numPoints = q;
5610   return 0;
5611 }
5612 
5613 /* Compressed closure does not apply closure permutation */
5614 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5615 {
5616   const PetscInt *cla = NULL;
5617   PetscInt       np, *pts = NULL;
5618 
5619   PetscFunctionBeginHot;
5620   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject) dm, clSec, clPoints));
5621   if (*clPoints) {
5622     PetscInt dof, off;
5623 
5624     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
5625     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
5626     PetscCall(ISGetIndices(*clPoints, &cla));
5627     np   = dof/2;
5628     pts  = (PetscInt *) &cla[off];
5629   } else {
5630     PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &np, &pts));
5631     PetscCall(CompressPoints_Private(section, &np, pts));
5632   }
5633   *numPoints = np;
5634   *points    = pts;
5635   *clp       = cla;
5636   PetscFunctionReturn(0);
5637 }
5638 
5639 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5640 {
5641   PetscFunctionBeginHot;
5642   if (!*clPoints) {
5643     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
5644   } else {
5645     PetscCall(ISRestoreIndices(*clPoints, clp));
5646   }
5647   *numPoints = 0;
5648   *points    = NULL;
5649   *clSec     = NULL;
5650   *clPoints  = NULL;
5651   *clp       = NULL;
5652   PetscFunctionReturn(0);
5653 }
5654 
5655 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
5656 {
5657   PetscInt          offset = 0, p;
5658   const PetscInt    **perms = NULL;
5659   const PetscScalar **flips = NULL;
5660 
5661   PetscFunctionBeginHot;
5662   *size = 0;
5663   PetscCall(PetscSectionGetPointSyms(section,numPoints,points,&perms,&flips));
5664   for (p = 0; p < numPoints; p++) {
5665     const PetscInt    point = points[2*p];
5666     const PetscInt    *perm = perms ? perms[p] : NULL;
5667     const PetscScalar *flip = flips ? flips[p] : NULL;
5668     PetscInt          dof, off, d;
5669     const PetscScalar *varr;
5670 
5671     PetscCall(PetscSectionGetDof(section, point, &dof));
5672     PetscCall(PetscSectionGetOffset(section, point, &off));
5673     varr = &vArray[off];
5674     if (clperm) {
5675       if (perm) {
5676         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]]  = varr[d];
5677       } else {
5678         for (d = 0; d < dof; d++) array[clperm[offset +      d ]]  = varr[d];
5679       }
5680       if (flip) {
5681         for (d = 0; d < dof; d++) array[clperm[offset +      d ]] *= flip[d];
5682       }
5683     } else {
5684       if (perm) {
5685         for (d = 0; d < dof; d++) array[offset + perm[d]]  = varr[d];
5686       } else {
5687         for (d = 0; d < dof; d++) array[offset +      d ]  = varr[d];
5688       }
5689       if (flip) {
5690         for (d = 0; d < dof; d++) array[offset +      d ] *= flip[d];
5691       }
5692     }
5693     offset += dof;
5694   }
5695   PetscCall(PetscSectionRestorePointSyms(section,numPoints,points,&perms,&flips));
5696   *size = offset;
5697   PetscFunctionReturn(0);
5698 }
5699 
5700 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[])
5701 {
5702   PetscInt          offset = 0, f;
5703 
5704   PetscFunctionBeginHot;
5705   *size = 0;
5706   for (f = 0; f < numFields; ++f) {
5707     PetscInt          p;
5708     const PetscInt    **perms = NULL;
5709     const PetscScalar **flips = NULL;
5710 
5711     PetscCall(PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips));
5712     for (p = 0; p < numPoints; p++) {
5713       const PetscInt    point = points[2*p];
5714       PetscInt          fdof, foff, b;
5715       const PetscScalar *varr;
5716       const PetscInt    *perm = perms ? perms[p] : NULL;
5717       const PetscScalar *flip = flips ? flips[p] : NULL;
5718 
5719       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
5720       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
5721       varr = &vArray[foff];
5722       if (clperm) {
5723         if (perm) {for (b = 0; b < fdof; b++) {array[clperm[offset + perm[b]]]  = varr[b];}}
5724         else      {for (b = 0; b < fdof; b++) {array[clperm[offset +      b ]]  = varr[b];}}
5725         if (flip) {for (b = 0; b < fdof; b++) {array[clperm[offset +      b ]] *= flip[b];}}
5726       } else {
5727         if (perm) {for (b = 0; b < fdof; b++) {array[offset + perm[b]]  = varr[b];}}
5728         else      {for (b = 0; b < fdof; b++) {array[offset +      b ]  = varr[b];}}
5729         if (flip) {for (b = 0; b < fdof; b++) {array[offset +      b ] *= flip[b];}}
5730       }
5731       offset += fdof;
5732     }
5733     PetscCall(PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips));
5734   }
5735   *size = offset;
5736   PetscFunctionReturn(0);
5737 }
5738 
5739 /*@C
5740   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
5741 
5742   Not collective
5743 
5744   Input Parameters:
5745 + dm - The DM
5746 . section - The section describing the layout in v, or NULL to use the default section
5747 . v - The local vector
5748 - point - The point in the DM
5749 
5750   Input/Output Parameters:
5751 + csize  - The size of the input values array, or NULL; on output the number of values in the closure
5752 - values - An array to use for the values, or NULL to have it allocated automatically;
5753            if the user provided NULL, it is a borrowed array and should not be freed
5754 
5755 $ Note that DMPlexVecGetClosure/DMPlexVecRestoreClosure only allocates the values array if it set to NULL in the
5756 $ calling function. This is because DMPlexVecGetClosure() is typically called in the inner loop of a Vec or Mat
5757 $ assembly function, and a user may already have allocated storage for this operation.
5758 $
5759 $ A typical use could be
5760 $
5761 $  values = NULL;
5762 $  PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5763 $  for (cl = 0; cl < clSize; ++cl) {
5764 $    <Compute on closure>
5765 $  }
5766 $  PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
5767 $
5768 $ or
5769 $
5770 $  PetscMalloc1(clMaxSize, &values);
5771 $  for (p = pStart; p < pEnd; ++p) {
5772 $    clSize = clMaxSize;
5773 $    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5774 $    for (cl = 0; cl < clSize; ++cl) {
5775 $      <Compute on closure>
5776 $    }
5777 $  }
5778 $  PetscFree(values);
5779 
5780   Fortran Notes:
5781   Since it returns an array, this routine is only available in Fortran 90, and you must
5782   include petsc.h90 in your code.
5783 
5784   The csize argument is not present in the Fortran 90 binding since it is internal to the array.
5785 
5786   Level: intermediate
5787 
5788 .seealso `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
5789 @*/
5790 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5791 {
5792   PetscSection       clSection;
5793   IS                 clPoints;
5794   PetscInt          *points = NULL;
5795   const PetscInt    *clp, *perm;
5796   PetscInt           depth, numFields, numPoints, asize;
5797 
5798   PetscFunctionBeginHot;
5799   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5800   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5801   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5802   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5803   PetscCall(DMPlexGetDepth(dm, &depth));
5804   PetscCall(PetscSectionGetNumFields(section, &numFields));
5805   if (depth == 1 && numFields < 2) {
5806     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
5807     PetscFunctionReturn(0);
5808   }
5809   /* Get points */
5810   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5811   /* Get sizes */
5812   asize = 0;
5813   for (PetscInt p = 0; p < numPoints*2; p += 2) {
5814     PetscInt dof;
5815     PetscCall(PetscSectionGetDof(section, points[p], &dof));
5816     asize += dof;
5817   }
5818   if (values) {
5819     const PetscScalar *vArray;
5820     PetscInt          size;
5821 
5822     if (*values) {
5823       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);
5824     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
5825     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, asize, &perm));
5826     PetscCall(VecGetArrayRead(v, &vArray));
5827     /* Get values */
5828     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
5829     else               PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
5830     PetscCheck(asize == size,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
5831     /* Cleanup array */
5832     PetscCall(VecRestoreArrayRead(v, &vArray));
5833   }
5834   if (csize) *csize = asize;
5835   /* Cleanup points */
5836   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5837   PetscFunctionReturn(0);
5838 }
5839 
5840 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
5841 {
5842   DMLabel            depthLabel;
5843   PetscSection       clSection;
5844   IS                 clPoints;
5845   PetscScalar       *array;
5846   const PetscScalar *vArray;
5847   PetscInt          *points = NULL;
5848   const PetscInt    *clp, *perm = NULL;
5849   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
5850 
5851   PetscFunctionBeginHot;
5852   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5853   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5854   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5855   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5856   PetscCall(DMPlexGetDepth(dm, &mdepth));
5857   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5858   PetscCall(PetscSectionGetNumFields(section, &numFields));
5859   if (mdepth == 1 && numFields < 2) {
5860     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
5861     PetscFunctionReturn(0);
5862   }
5863   /* Get points */
5864   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5865   for (clsize=0,p=0; p<Np; p++) {
5866     PetscInt dof;
5867     PetscCall(PetscSectionGetDof(section, points[2*p], &dof));
5868     clsize += dof;
5869   }
5870   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, clsize, &perm));
5871   /* Filter points */
5872   for (p = 0; p < numPoints*2; p += 2) {
5873     PetscInt dep;
5874 
5875     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
5876     if (dep != depth) continue;
5877     points[Np*2+0] = points[p];
5878     points[Np*2+1] = points[p+1];
5879     ++Np;
5880   }
5881   /* Get array */
5882   if (!values || !*values) {
5883     PetscInt asize = 0, dof;
5884 
5885     for (p = 0; p < Np*2; p += 2) {
5886       PetscCall(PetscSectionGetDof(section, points[p], &dof));
5887       asize += dof;
5888     }
5889     if (!values) {
5890       PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5891       if (csize) *csize = asize;
5892       PetscFunctionReturn(0);
5893     }
5894     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
5895   } else {
5896     array = *values;
5897   }
5898   PetscCall(VecGetArrayRead(v, &vArray));
5899   /* Get values */
5900   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
5901   else               PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
5902   /* Cleanup points */
5903   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
5904   /* Cleanup array */
5905   PetscCall(VecRestoreArrayRead(v, &vArray));
5906   if (!*values) {
5907     if (csize) *csize = size;
5908     *values = array;
5909   } else {
5910     PetscCheck(size <= *csize,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5911     *csize = size;
5912   }
5913   PetscFunctionReturn(0);
5914 }
5915 
5916 /*@C
5917   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
5918 
5919   Not collective
5920 
5921   Input Parameters:
5922 + dm - The DM
5923 . section - The section describing the layout in v, or NULL to use the default section
5924 . v - The local vector
5925 . point - The point in the DM
5926 . csize - The number of values in the closure, or NULL
5927 - values - The array of values, which is a borrowed array and should not be freed
5928 
5929   Note that the array values are discarded and not copied back into v. In order to copy values back to v, use DMPlexVecSetClosure()
5930 
5931   Fortran Notes:
5932   Since it returns an array, this routine is only available in Fortran 90, and you must
5933   include petsc.h90 in your code.
5934 
5935   The csize argument is not present in the Fortran 90 binding since it is internal to the array.
5936 
5937   Level: intermediate
5938 
5939 .seealso `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
5940 @*/
5941 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5942 {
5943   PetscInt       size = 0;
5944 
5945   PetscFunctionBegin;
5946   /* Should work without recalculating size */
5947   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void*) values));
5948   *values = NULL;
5949   PetscFunctionReturn(0);
5950 }
5951 
5952 static inline void add   (PetscScalar *x, PetscScalar y) {*x += y;}
5953 static inline void insert(PetscScalar *x, PetscScalar y) {*x  = y;}
5954 
5955 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[])
5956 {
5957   PetscInt        cdof;   /* The number of constraints on this point */
5958   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
5959   PetscScalar    *a;
5960   PetscInt        off, cind = 0, k;
5961 
5962   PetscFunctionBegin;
5963   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
5964   PetscCall(PetscSectionGetOffset(section, point, &off));
5965   a    = &array[off];
5966   if (!cdof || setBC) {
5967     if (clperm) {
5968       if (perm) {for (k = 0; k < dof; ++k) {fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));}}
5969       else      {for (k = 0; k < dof; ++k) {fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));}}
5970     } else {
5971       if (perm) {for (k = 0; k < dof; ++k) {fuse(&a[k], values[offset+perm[k]] * (flip ? flip[perm[k]] : 1.));}}
5972       else      {for (k = 0; k < dof; ++k) {fuse(&a[k], values[offset+     k ] * (flip ? flip[     k ] : 1.));}}
5973     }
5974   } else {
5975     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
5976     if (clperm) {
5977       if (perm) {for (k = 0; k < dof; ++k) {
5978           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5979           fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));
5980         }
5981       } else {
5982         for (k = 0; k < dof; ++k) {
5983           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5984           fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));
5985         }
5986       }
5987     } else {
5988       if (perm) {
5989         for (k = 0; k < dof; ++k) {
5990           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5991           fuse(&a[k], values[offset+perm[k]] * (flip ? flip[perm[k]] : 1.));
5992         }
5993       } else {
5994         for (k = 0; k < dof; ++k) {
5995           if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
5996           fuse(&a[k], values[offset+     k ] * (flip ? flip[     k ] : 1.));
5997         }
5998       }
5999     }
6000   }
6001   PetscFunctionReturn(0);
6002 }
6003 
6004 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[])
6005 {
6006   PetscInt        cdof;   /* The number of constraints on this point */
6007   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6008   PetscScalar    *a;
6009   PetscInt        off, cind = 0, k;
6010 
6011   PetscFunctionBegin;
6012   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6013   PetscCall(PetscSectionGetOffset(section, point, &off));
6014   a    = &array[off];
6015   if (cdof) {
6016     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6017     if (clperm) {
6018       if (perm) {
6019         for (k = 0; k < dof; ++k) {
6020           if ((cind < cdof) && (k == cdofs[cind])) {
6021             fuse(&a[k], values[clperm[offset+perm[k]]] * (flip ? flip[perm[k]] : 1.));
6022             cind++;
6023           }
6024         }
6025       } else {
6026         for (k = 0; k < dof; ++k) {
6027           if ((cind < cdof) && (k == cdofs[cind])) {
6028             fuse(&a[k], values[clperm[offset+     k ]] * (flip ? flip[     k ] : 1.));
6029             cind++;
6030           }
6031         }
6032       }
6033     } else {
6034       if (perm) {
6035         for (k = 0; k < dof; ++k) {
6036           if ((cind < cdof) && (k == cdofs[cind])) {
6037             fuse(&a[k], values[offset+perm[k]] * (flip ? flip[perm[k]] : 1.));
6038             cind++;
6039           }
6040         }
6041       } else {
6042         for (k = 0; k < dof; ++k) {
6043           if ((cind < cdof) && (k == cdofs[cind])) {
6044             fuse(&a[k], values[offset+     k ] * (flip ? flip[     k ] : 1.));
6045             cind++;
6046           }
6047         }
6048       }
6049     }
6050   }
6051   PetscFunctionReturn(0);
6052 }
6053 
6054 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[])
6055 {
6056   PetscScalar    *a;
6057   PetscInt        fdof, foff, fcdof, foffset = *offset;
6058   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6059   PetscInt        cind = 0, b;
6060 
6061   PetscFunctionBegin;
6062   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6063   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6064   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6065   a    = &array[foff];
6066   if (!fcdof || setBC) {
6067     if (clperm) {
6068       if (perm) {for (b = 0; b < fdof; b++) {fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));}}
6069       else      {for (b = 0; b < fdof; b++) {fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));}}
6070     } else {
6071       if (perm) {for (b = 0; b < fdof; b++) {fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));}}
6072       else      {for (b = 0; b < fdof; b++) {fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));}}
6073     }
6074   } else {
6075     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6076     if (clperm) {
6077       if (perm) {
6078         for (b = 0; b < fdof; b++) {
6079           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6080           fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));
6081         }
6082       } else {
6083         for (b = 0; b < fdof; b++) {
6084           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6085           fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));
6086         }
6087       }
6088     } else {
6089       if (perm) {
6090         for (b = 0; b < fdof; b++) {
6091           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6092           fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));
6093         }
6094       } else {
6095         for (b = 0; b < fdof; b++) {
6096           if ((cind < fcdof) && (b == fcdofs[cind])) {++cind; continue;}
6097           fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));
6098         }
6099       }
6100     }
6101   }
6102   *offset += fdof;
6103   PetscFunctionReturn(0);
6104 }
6105 
6106 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[])
6107 {
6108   PetscScalar    *a;
6109   PetscInt        fdof, foff, fcdof, foffset = *offset;
6110   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6111   PetscInt        Nc, cind = 0, ncind = 0, b;
6112   PetscBool       ncSet, fcSet;
6113 
6114   PetscFunctionBegin;
6115   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6116   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6117   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6118   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6119   a    = &array[foff];
6120   if (fcdof) {
6121     /* We just override fcdof and fcdofs with Ncc and comps */
6122     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6123     if (clperm) {
6124       if (perm) {
6125         if (comps) {
6126           for (b = 0; b < fdof; b++) {
6127             ncSet = fcSet = PETSC_FALSE;
6128             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6129             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6130             if (ncSet && fcSet) {fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));}
6131           }
6132         } else {
6133           for (b = 0; b < fdof; b++) {
6134             if ((cind < fcdof) && (b == fcdofs[cind])) {
6135               fuse(&a[b], values[clperm[foffset+perm[b]]] * (flip ? flip[perm[b]] : 1.));
6136               ++cind;
6137             }
6138           }
6139         }
6140       } else {
6141         if (comps) {
6142           for (b = 0; b < fdof; b++) {
6143             ncSet = fcSet = PETSC_FALSE;
6144             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6145             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6146             if (ncSet && fcSet) {fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));}
6147           }
6148         } else {
6149           for (b = 0; b < fdof; b++) {
6150             if ((cind < fcdof) && (b == fcdofs[cind])) {
6151               fuse(&a[b], values[clperm[foffset+     b ]] * (flip ? flip[     b ] : 1.));
6152               ++cind;
6153             }
6154           }
6155         }
6156       }
6157     } else {
6158       if (perm) {
6159         if (comps) {
6160           for (b = 0; b < fdof; b++) {
6161             ncSet = fcSet = PETSC_FALSE;
6162             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6163             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6164             if (ncSet && fcSet) {fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));}
6165           }
6166         } else {
6167           for (b = 0; b < fdof; b++) {
6168             if ((cind < fcdof) && (b == fcdofs[cind])) {
6169               fuse(&a[b], values[foffset+perm[b]] * (flip ? flip[perm[b]] : 1.));
6170               ++cind;
6171             }
6172           }
6173         }
6174       } else {
6175         if (comps) {
6176           for (b = 0; b < fdof; b++) {
6177             ncSet = fcSet = PETSC_FALSE;
6178             if (b%Nc == comps[ncind]) {ncind = (ncind+1)%Ncc; ncSet = PETSC_TRUE;}
6179             if ((cind < fcdof) && (b == fcdofs[cind])) {++cind;  fcSet = PETSC_TRUE;}
6180             if (ncSet && fcSet) {fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));}
6181           }
6182         } else {
6183           for (b = 0; b < fdof; b++) {
6184             if ((cind < fcdof) && (b == fcdofs[cind])) {
6185               fuse(&a[b], values[foffset+     b ] * (flip ? flip[     b ] : 1.));
6186               ++cind;
6187             }
6188           }
6189         }
6190       }
6191     }
6192   }
6193   *offset += fdof;
6194   PetscFunctionReturn(0);
6195 }
6196 
6197 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6198 {
6199   PetscScalar    *array;
6200   const PetscInt *cone, *coneO;
6201   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6202 
6203   PetscFunctionBeginHot;
6204   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6205   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6206   PetscCall(DMPlexGetCone(dm, point, &cone));
6207   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6208   PetscCall(VecGetArray(v, &array));
6209   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6210     const PetscInt cp = !p ? point : cone[p-1];
6211     const PetscInt o  = !p ? 0     : coneO[p-1];
6212 
6213     if ((cp < pStart) || (cp >= pEnd)) {dof = 0; continue;}
6214     PetscCall(PetscSectionGetDof(section, cp, &dof));
6215     /* ADD_VALUES */
6216     {
6217       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6218       PetscScalar    *a;
6219       PetscInt        cdof, coff, cind = 0, k;
6220 
6221       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6222       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6223       a    = &array[coff];
6224       if (!cdof) {
6225         if (o >= 0) {
6226           for (k = 0; k < dof; ++k) {
6227             a[k] += values[off+k];
6228           }
6229         } else {
6230           for (k = 0; k < dof; ++k) {
6231             a[k] += values[off+dof-k-1];
6232           }
6233         }
6234       } else {
6235         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6236         if (o >= 0) {
6237           for (k = 0; k < dof; ++k) {
6238             if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
6239             a[k] += values[off+k];
6240           }
6241         } else {
6242           for (k = 0; k < dof; ++k) {
6243             if ((cind < cdof) && (k == cdofs[cind])) {++cind; continue;}
6244             a[k] += values[off+dof-k-1];
6245           }
6246         }
6247       }
6248     }
6249   }
6250   PetscCall(VecRestoreArray(v, &array));
6251   PetscFunctionReturn(0);
6252 }
6253 
6254 /*@C
6255   DMPlexVecSetClosure - Set an array of the values on the closure of 'point'
6256 
6257   Not collective
6258 
6259   Input Parameters:
6260 + dm - The DM
6261 . section - The section describing the layout in v, or NULL to use the default section
6262 . v - The local vector
6263 . point - The point in the DM
6264 . values - The array of values
6265 - mode - The insert mode. One of INSERT_ALL_VALUES, ADD_ALL_VALUES, INSERT_VALUES, ADD_VALUES, INSERT_BC_VALUES, and ADD_BC_VALUES,
6266          where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions.
6267 
6268   Fortran Notes:
6269   This routine is only available in Fortran 90, and you must include petsc.h90 in your code.
6270 
6271   Level: intermediate
6272 
6273 .seealso `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6274 @*/
6275 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6276 {
6277   PetscSection    clSection;
6278   IS              clPoints;
6279   PetscScalar    *array;
6280   PetscInt       *points = NULL;
6281   const PetscInt *clp, *clperm = NULL;
6282   PetscInt        depth, numFields, numPoints, p, clsize;
6283 
6284   PetscFunctionBeginHot;
6285   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6286   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6287   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6288   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6289   PetscCall(DMPlexGetDepth(dm, &depth));
6290   PetscCall(PetscSectionGetNumFields(section, &numFields));
6291   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6292     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6293     PetscFunctionReturn(0);
6294   }
6295   /* Get points */
6296   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6297   for (clsize=0,p=0; p<numPoints; p++) {
6298     PetscInt dof;
6299     PetscCall(PetscSectionGetDof(section, points[2*p], &dof));
6300     clsize += dof;
6301   }
6302   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, clsize, &clperm));
6303   /* Get array */
6304   PetscCall(VecGetArray(v, &array));
6305   /* Get values */
6306   if (numFields > 0) {
6307     PetscInt offset = 0, f;
6308     for (f = 0; f < numFields; ++f) {
6309       const PetscInt    **perms = NULL;
6310       const PetscScalar **flips = NULL;
6311 
6312       PetscCall(PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6313       switch (mode) {
6314       case INSERT_VALUES:
6315         for (p = 0; p < numPoints; p++) {
6316           const PetscInt    point = points[2*p];
6317           const PetscInt    *perm = perms ? perms[p] : NULL;
6318           const PetscScalar *flip = flips ? flips[p] : NULL;
6319           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array);
6320         } break;
6321       case INSERT_ALL_VALUES:
6322         for (p = 0; p < numPoints; p++) {
6323           const PetscInt    point = points[2*p];
6324           const PetscInt    *perm = perms ? perms[p] : NULL;
6325           const PetscScalar *flip = flips ? flips[p] : NULL;
6326           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array);
6327         } break;
6328       case INSERT_BC_VALUES:
6329         for (p = 0; p < numPoints; p++) {
6330           const PetscInt    point = points[2*p];
6331           const PetscInt    *perm = perms ? perms[p] : NULL;
6332           const PetscScalar *flip = flips ? flips[p] : NULL;
6333           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array);
6334         } break;
6335       case ADD_VALUES:
6336         for (p = 0; p < numPoints; p++) {
6337           const PetscInt    point = points[2*p];
6338           const PetscInt    *perm = perms ? perms[p] : NULL;
6339           const PetscScalar *flip = flips ? flips[p] : NULL;
6340           updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array);
6341         } break;
6342       case ADD_ALL_VALUES:
6343         for (p = 0; p < numPoints; p++) {
6344           const PetscInt    point = points[2*p];
6345           const PetscInt    *perm = perms ? perms[p] : NULL;
6346           const PetscScalar *flip = flips ? flips[p] : NULL;
6347           updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array);
6348         } break;
6349       case ADD_BC_VALUES:
6350         for (p = 0; p < numPoints; p++) {
6351           const PetscInt    point = points[2*p];
6352           const PetscInt    *perm = perms ? perms[p] : NULL;
6353           const PetscScalar *flip = flips ? flips[p] : NULL;
6354           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array);
6355         } break;
6356       default:
6357         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6358       }
6359       PetscCall(PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6360     }
6361   } else {
6362     PetscInt dof, off;
6363     const PetscInt    **perms = NULL;
6364     const PetscScalar **flips = NULL;
6365 
6366     PetscCall(PetscSectionGetPointSyms(section,numPoints,points,&perms,&flips));
6367     switch (mode) {
6368     case INSERT_VALUES:
6369       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6370         const PetscInt    point = points[2*p];
6371         const PetscInt    *perm = perms ? perms[p] : NULL;
6372         const PetscScalar *flip = flips ? flips[p] : NULL;
6373         PetscCall(PetscSectionGetDof(section, point, &dof));
6374         updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array);
6375       } break;
6376     case INSERT_ALL_VALUES:
6377       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6378         const PetscInt    point = points[2*p];
6379         const PetscInt    *perm = perms ? perms[p] : NULL;
6380         const PetscScalar *flip = flips ? flips[p] : NULL;
6381         PetscCall(PetscSectionGetDof(section, point, &dof));
6382         updatePoint_private(section, point, dof, insert, PETSC_TRUE,  perm, flip, clperm, values, off, array);
6383       } break;
6384     case INSERT_BC_VALUES:
6385       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6386         const PetscInt    point = points[2*p];
6387         const PetscInt    *perm = perms ? perms[p] : NULL;
6388         const PetscScalar *flip = flips ? flips[p] : NULL;
6389         PetscCall(PetscSectionGetDof(section, point, &dof));
6390         updatePointBC_private(section, point, dof, insert,  perm, flip, clperm, values, off, array);
6391       } break;
6392     case ADD_VALUES:
6393       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6394         const PetscInt    point = points[2*p];
6395         const PetscInt    *perm = perms ? perms[p] : NULL;
6396         const PetscScalar *flip = flips ? flips[p] : NULL;
6397         PetscCall(PetscSectionGetDof(section, point, &dof));
6398         updatePoint_private(section, point, dof, add,    PETSC_FALSE, perm, flip, clperm, values, off, array);
6399       } break;
6400     case ADD_ALL_VALUES:
6401       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6402         const PetscInt    point = points[2*p];
6403         const PetscInt    *perm = perms ? perms[p] : NULL;
6404         const PetscScalar *flip = flips ? flips[p] : NULL;
6405         PetscCall(PetscSectionGetDof(section, point, &dof));
6406         updatePoint_private(section, point, dof, add,    PETSC_TRUE,  perm, flip, clperm, values, off, array);
6407       } break;
6408     case ADD_BC_VALUES:
6409       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6410         const PetscInt    point = points[2*p];
6411         const PetscInt    *perm = perms ? perms[p] : NULL;
6412         const PetscScalar *flip = flips ? flips[p] : NULL;
6413         PetscCall(PetscSectionGetDof(section, point, &dof));
6414         updatePointBC_private(section, point, dof, add,  perm, flip, clperm, values, off, array);
6415       } break;
6416     default:
6417       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6418     }
6419     PetscCall(PetscSectionRestorePointSyms(section,numPoints,points,&perms,&flips));
6420   }
6421   /* Cleanup points */
6422   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6423   /* Cleanup array */
6424   PetscCall(VecRestoreArray(v, &array));
6425   PetscFunctionReturn(0);
6426 }
6427 
6428 /* Check whether the given point is in the label. If not, update the offset to skip this point */
6429 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset)
6430 {
6431   PetscFunctionBegin;
6432   if (label) {
6433     PetscInt       val, fdof;
6434 
6435     /* There is a problem with this:
6436          Suppose we have two label values, defining surfaces, interecting along a line in 3D. When we add cells to the label, the cells that
6437        touch both surfaces must pick a label value. Thus we miss setting values for the surface with that other value intersecting that cell.
6438        Thus I am only going to check val != -1, not val != labelId
6439     */
6440     PetscCall(DMLabelGetValue(label, point, &val));
6441     if (val < 0) {
6442       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6443       *offset += fdof;
6444       PetscFunctionReturn(1);
6445     }
6446   }
6447   PetscFunctionReturn(0);
6448 }
6449 
6450 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
6451 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)
6452 {
6453   PetscSection    clSection;
6454   IS              clPoints;
6455   PetscScalar    *array;
6456   PetscInt       *points = NULL;
6457   const PetscInt *clp;
6458   PetscInt        numFields, numPoints, p;
6459   PetscInt        offset = 0, f;
6460 
6461   PetscFunctionBeginHot;
6462   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6463   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6464   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6465   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6466   PetscCall(PetscSectionGetNumFields(section, &numFields));
6467   /* Get points */
6468   PetscCall(DMPlexGetCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6469   /* Get array */
6470   PetscCall(VecGetArray(v, &array));
6471   /* Get values */
6472   for (f = 0; f < numFields; ++f) {
6473     const PetscInt    **perms = NULL;
6474     const PetscScalar **flips = NULL;
6475 
6476     if (!fieldActive[f]) {
6477       for (p = 0; p < numPoints*2; p += 2) {
6478         PetscInt fdof;
6479         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
6480         offset += fdof;
6481       }
6482       continue;
6483     }
6484     PetscCall(PetscSectionGetFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6485     switch (mode) {
6486     case INSERT_VALUES:
6487       for (p = 0; p < numPoints; p++) {
6488         const PetscInt    point = points[2*p];
6489         const PetscInt    *perm = perms ? perms[p] : NULL;
6490         const PetscScalar *flip = flips ? flips[p] : NULL;
6491         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6492         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
6493       } break;
6494     case INSERT_ALL_VALUES:
6495       for (p = 0; p < numPoints; p++) {
6496         const PetscInt    point = points[2*p];
6497         const PetscInt    *perm = perms ? perms[p] : NULL;
6498         const PetscScalar *flip = flips ? flips[p] : NULL;
6499         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6500         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
6501       } break;
6502     case INSERT_BC_VALUES:
6503       for (p = 0; p < numPoints; p++) {
6504         const PetscInt    point = points[2*p];
6505         const PetscInt    *perm = perms ? perms[p] : NULL;
6506         const PetscScalar *flip = flips ? flips[p] : NULL;
6507         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6508         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
6509       } break;
6510     case ADD_VALUES:
6511       for (p = 0; p < numPoints; p++) {
6512         const PetscInt    point = points[2*p];
6513         const PetscInt    *perm = perms ? perms[p] : NULL;
6514         const PetscScalar *flip = flips ? flips[p] : NULL;
6515         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6516         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
6517       } break;
6518     case ADD_ALL_VALUES:
6519       for (p = 0; p < numPoints; p++) {
6520         const PetscInt    point = points[2*p];
6521         const PetscInt    *perm = perms ? perms[p] : NULL;
6522         const PetscScalar *flip = flips ? flips[p] : NULL;
6523         if (CheckPoint_Private(label, labelId, section, point, f, &offset)) continue;
6524         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
6525       } break;
6526     default:
6527       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6528     }
6529     PetscCall(PetscSectionRestoreFieldPointSyms(section,f,numPoints,points,&perms,&flips));
6530   }
6531   /* Cleanup points */
6532   PetscCall(DMPlexRestoreCompressedClosure(dm,section,point,&numPoints,&points,&clSection,&clPoints,&clp));
6533   /* Cleanup array */
6534   PetscCall(VecRestoreArray(v, &array));
6535   PetscFunctionReturn(0);
6536 }
6537 
6538 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
6539 {
6540   PetscMPIInt    rank;
6541   PetscInt       i, j;
6542 
6543   PetscFunctionBegin;
6544   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
6545   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
6546   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
6547   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
6548   numCIndices = numCIndices ? numCIndices : numRIndices;
6549   if (!values) PetscFunctionReturn(0);
6550   for (i = 0; i < numRIndices; i++) {
6551     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
6552     for (j = 0; j < numCIndices; j++) {
6553 #if defined(PETSC_USE_COMPLEX)
6554       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i*numCIndices+j]), (double)PetscImaginaryPart(values[i*numCIndices+j])));
6555 #else
6556       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i*numCIndices+j]));
6557 #endif
6558     }
6559     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
6560   }
6561   PetscFunctionReturn(0);
6562 }
6563 
6564 /*
6565   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
6566 
6567   Input Parameters:
6568 + section - The section for this data layout
6569 . islocal - Is the section (and thus indices being requested) local or global?
6570 . point   - The point contributing dofs with these indices
6571 . off     - The global offset of this point
6572 . loff    - The local offset of each field
6573 . setBC   - The flag determining whether to include indices of boundary values
6574 . perm    - A permutation of the dofs on this point, or NULL
6575 - indperm - A permutation of the entire indices array, or NULL
6576 
6577   Output Parameter:
6578 . indices - Indices for dofs on this point
6579 
6580   Level: developer
6581 
6582   Note: The indices could be local or global, depending on the value of 'off'.
6583 */
6584 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal,PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
6585 {
6586   PetscInt        dof;   /* The number of unknowns on this point */
6587   PetscInt        cdof;  /* The number of constraints on this point */
6588   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6589   PetscInt        cind = 0, k;
6590 
6591   PetscFunctionBegin;
6592   PetscCheck(islocal || !setBC,PetscObjectComm((PetscObject)section),PETSC_ERR_ARG_INCOMP,"setBC incompatible with global indices; use a local section or disable setBC");
6593   PetscCall(PetscSectionGetDof(section, point, &dof));
6594   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6595   if (!cdof || setBC) {
6596     for (k = 0; k < dof; ++k) {
6597       const PetscInt preind = perm ? *loff+perm[k] : *loff+k;
6598       const PetscInt ind    = indperm ? indperm[preind] : preind;
6599 
6600       indices[ind] = off + k;
6601     }
6602   } else {
6603     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6604     for (k = 0; k < dof; ++k) {
6605       const PetscInt preind = perm ? *loff+perm[k] : *loff+k;
6606       const PetscInt ind    = indperm ? indperm[preind] : preind;
6607 
6608       if ((cind < cdof) && (k == cdofs[cind])) {
6609         /* Insert check for returning constrained indices */
6610         indices[ind] = -(off+k+1);
6611         ++cind;
6612       } else {
6613         indices[ind] = off + k - (islocal ? 0 : cind);
6614       }
6615     }
6616   }
6617   *loff += dof;
6618   PetscFunctionReturn(0);
6619 }
6620 
6621 /*
6622  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
6623 
6624  Input Parameters:
6625 + section - a section (global or local)
6626 - islocal - PETSC_TRUE if requesting local indices (i.e., section is local); PETSC_FALSE for global
6627 . point - point within section
6628 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
6629 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
6630 . setBC - identify constrained (boundary condition) points via involution.
6631 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
6632 . permsoff - offset
6633 - indperm - index permutation
6634 
6635  Output Parameter:
6636 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
6637 . indices - array to hold indices (as defined by section) of each dof associated with point
6638 
6639  Notes:
6640  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
6641  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
6642  in the local vector.
6643 
6644  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
6645  significant).  It is invalid to call with a global section and setBC=true.
6646 
6647  Developer Note:
6648  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
6649  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
6650  offset could be obtained from the section instead of passing it explicitly as we do now.
6651 
6652  Example:
6653  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
6654  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
6655  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
6656  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.
6657 
6658  Level: developer
6659 */
6660 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[])
6661 {
6662   PetscInt       numFields, foff, f;
6663 
6664   PetscFunctionBegin;
6665   PetscCheck(islocal || !setBC,PetscObjectComm((PetscObject)section),PETSC_ERR_ARG_INCOMP,"setBC incompatible with global indices; use a local section or disable setBC");
6666   PetscCall(PetscSectionGetNumFields(section, &numFields));
6667   for (f = 0, foff = 0; f < numFields; ++f) {
6668     PetscInt        fdof, cfdof;
6669     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6670     PetscInt        cind = 0, b;
6671     const PetscInt  *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6672 
6673     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6674     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
6675     if (!cfdof || setBC) {
6676       for (b = 0; b < fdof; ++b) {
6677         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6678         const PetscInt ind    = indperm ? indperm[preind] : preind;
6679 
6680         indices[ind] = off+foff+b;
6681       }
6682     } else {
6683       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6684       for (b = 0; b < fdof; ++b) {
6685         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6686         const PetscInt ind    = indperm ? indperm[preind] : preind;
6687 
6688         if ((cind < cfdof) && (b == fcdofs[cind])) {
6689           indices[ind] = -(off+foff+b+1);
6690           ++cind;
6691         } else {
6692           indices[ind] = off + foff + b - (islocal ? 0 : cind);
6693         }
6694       }
6695     }
6696     foff     += (setBC || islocal ? fdof : (fdof - cfdof));
6697     foffs[f] += fdof;
6698   }
6699   PetscFunctionReturn(0);
6700 }
6701 
6702 /*
6703   This version believes the globalSection offsets for each field, rather than just the point offset
6704 
6705  . foffs - The offset into 'indices' for each field, since it is segregated by field
6706 
6707  Notes:
6708  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
6709  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
6710 */
6711 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
6712 {
6713   PetscInt       numFields, foff, f;
6714 
6715   PetscFunctionBegin;
6716   PetscCall(PetscSectionGetNumFields(section, &numFields));
6717   for (f = 0; f < numFields; ++f) {
6718     PetscInt        fdof, cfdof;
6719     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6720     PetscInt        cind = 0, b;
6721     const PetscInt  *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6722 
6723     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6724     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
6725     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
6726     if (!cfdof) {
6727       for (b = 0; b < fdof; ++b) {
6728         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6729         const PetscInt ind    = indperm ? indperm[preind] : preind;
6730 
6731         indices[ind] = foff+b;
6732       }
6733     } else {
6734       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6735       for (b = 0; b < fdof; ++b) {
6736         const PetscInt preind = perm ? foffs[f]+perm[b] : foffs[f]+b;
6737         const PetscInt ind    = indperm ? indperm[preind] : preind;
6738 
6739         if ((cind < cfdof) && (b == fcdofs[cind])) {
6740           indices[ind] = -(foff+b+1);
6741           ++cind;
6742         } else {
6743           indices[ind] = foff+b-cind;
6744         }
6745       }
6746     }
6747     foffs[f] += fdof;
6748   }
6749   PetscFunctionReturn(0);
6750 }
6751 
6752 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)
6753 {
6754   Mat             cMat;
6755   PetscSection    aSec, cSec;
6756   IS              aIS;
6757   PetscInt        aStart = -1, aEnd = -1;
6758   const PetscInt  *anchors;
6759   PetscInt        numFields, f, p, q, newP = 0;
6760   PetscInt        newNumPoints = 0, newNumIndices = 0;
6761   PetscInt        *newPoints, *indices, *newIndices;
6762   PetscInt        maxAnchor, maxDof;
6763   PetscInt        newOffsets[32];
6764   PetscInt        *pointMatOffsets[32];
6765   PetscInt        *newPointOffsets[32];
6766   PetscScalar     *pointMat[32];
6767   PetscScalar     *newValues=NULL,*tmpValues;
6768   PetscBool       anyConstrained = PETSC_FALSE;
6769 
6770   PetscFunctionBegin;
6771   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6772   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6773   PetscCall(PetscSectionGetNumFields(section, &numFields));
6774 
6775   PetscCall(DMPlexGetAnchors(dm,&aSec,&aIS));
6776   /* if there are point-to-point constraints */
6777   if (aSec) {
6778     PetscCall(PetscArrayzero(newOffsets, 32));
6779     PetscCall(ISGetIndices(aIS,&anchors));
6780     PetscCall(PetscSectionGetChart(aSec,&aStart,&aEnd));
6781     /* figure out how many points are going to be in the new element matrix
6782      * (we allow double counting, because it's all just going to be summed
6783      * into the global matrix anyway) */
6784     for (p = 0; p < 2*numPoints; p+=2) {
6785       PetscInt b    = points[p];
6786       PetscInt bDof = 0, bSecDof;
6787 
6788       PetscCall(PetscSectionGetDof(section,b,&bSecDof));
6789       if (!bSecDof) {
6790         continue;
6791       }
6792       if (b >= aStart && b < aEnd) {
6793         PetscCall(PetscSectionGetDof(aSec,b,&bDof));
6794       }
6795       if (bDof) {
6796         /* this point is constrained */
6797         /* it is going to be replaced by its anchors */
6798         PetscInt bOff, q;
6799 
6800         anyConstrained = PETSC_TRUE;
6801         newNumPoints  += bDof;
6802         PetscCall(PetscSectionGetOffset(aSec,b,&bOff));
6803         for (q = 0; q < bDof; q++) {
6804           PetscInt a = anchors[bOff + q];
6805           PetscInt aDof;
6806 
6807           PetscCall(PetscSectionGetDof(section,a,&aDof));
6808           newNumIndices += aDof;
6809           for (f = 0; f < numFields; ++f) {
6810             PetscInt fDof;
6811 
6812             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
6813             newOffsets[f+1] += fDof;
6814           }
6815         }
6816       }
6817       else {
6818         /* this point is not constrained */
6819         newNumPoints++;
6820         newNumIndices += bSecDof;
6821         for (f = 0; f < numFields; ++f) {
6822           PetscInt fDof;
6823 
6824           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6825           newOffsets[f+1] += fDof;
6826         }
6827       }
6828     }
6829   }
6830   if (!anyConstrained) {
6831     if (outNumPoints)  *outNumPoints  = 0;
6832     if (outNumIndices) *outNumIndices = 0;
6833     if (outPoints)     *outPoints     = NULL;
6834     if (outValues)     *outValues     = NULL;
6835     if (aSec) PetscCall(ISRestoreIndices(aIS,&anchors));
6836     PetscFunctionReturn(0);
6837   }
6838 
6839   if (outNumPoints)  *outNumPoints  = newNumPoints;
6840   if (outNumIndices) *outNumIndices = newNumIndices;
6841 
6842   for (f = 0; f < numFields; ++f) newOffsets[f+1] += newOffsets[f];
6843 
6844   if (!outPoints && !outValues) {
6845     if (offsets) {
6846       for (f = 0; f <= numFields; f++) {
6847         offsets[f] = newOffsets[f];
6848       }
6849     }
6850     if (aSec) PetscCall(ISRestoreIndices(aIS,&anchors));
6851     PetscFunctionReturn(0);
6852   }
6853 
6854   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
6855 
6856   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
6857 
6858   /* workspaces */
6859   if (numFields) {
6860     for (f = 0; f < numFields; f++) {
6861       PetscCall(DMGetWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[f]));
6862       PetscCall(DMGetWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[f]));
6863     }
6864   }
6865   else {
6866     PetscCall(DMGetWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[0]));
6867     PetscCall(DMGetWorkArray(dm,numPoints,MPIU_INT,&newPointOffsets[0]));
6868   }
6869 
6870   /* get workspaces for the point-to-point matrices */
6871   if (numFields) {
6872     PetscInt totalOffset, totalMatOffset;
6873 
6874     for (p = 0; p < numPoints; p++) {
6875       PetscInt b    = points[2*p];
6876       PetscInt bDof = 0, bSecDof;
6877 
6878       PetscCall(PetscSectionGetDof(section,b,&bSecDof));
6879       if (!bSecDof) {
6880         for (f = 0; f < numFields; f++) {
6881           newPointOffsets[f][p + 1] = 0;
6882           pointMatOffsets[f][p + 1] = 0;
6883         }
6884         continue;
6885       }
6886       if (b >= aStart && b < aEnd) {
6887         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6888       }
6889       if (bDof) {
6890         for (f = 0; f < numFields; f++) {
6891           PetscInt fDof, q, bOff, allFDof = 0;
6892 
6893           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6894           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6895           for (q = 0; q < bDof; q++) {
6896             PetscInt a = anchors[bOff + q];
6897             PetscInt aFDof;
6898 
6899             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
6900             allFDof += aFDof;
6901           }
6902           newPointOffsets[f][p+1] = allFDof;
6903           pointMatOffsets[f][p+1] = fDof * allFDof;
6904         }
6905       }
6906       else {
6907         for (f = 0; f < numFields; f++) {
6908           PetscInt fDof;
6909 
6910           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6911           newPointOffsets[f][p+1] = fDof;
6912           pointMatOffsets[f][p+1] = 0;
6913         }
6914       }
6915     }
6916     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
6917       newPointOffsets[f][0] = totalOffset;
6918       pointMatOffsets[f][0] = totalMatOffset;
6919       for (p = 0; p < numPoints; p++) {
6920         newPointOffsets[f][p+1] += newPointOffsets[f][p];
6921         pointMatOffsets[f][p+1] += pointMatOffsets[f][p];
6922       }
6923       totalOffset    = newPointOffsets[f][numPoints];
6924       totalMatOffset = pointMatOffsets[f][numPoints];
6925       PetscCall(DMGetWorkArray(dm,pointMatOffsets[f][numPoints],MPIU_SCALAR,&pointMat[f]));
6926     }
6927   }
6928   else {
6929     for (p = 0; p < numPoints; p++) {
6930       PetscInt b    = points[2*p];
6931       PetscInt bDof = 0, bSecDof;
6932 
6933       PetscCall(PetscSectionGetDof(section,b,&bSecDof));
6934       if (!bSecDof) {
6935         newPointOffsets[0][p + 1] = 0;
6936         pointMatOffsets[0][p + 1] = 0;
6937         continue;
6938       }
6939       if (b >= aStart && b < aEnd) {
6940         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6941       }
6942       if (bDof) {
6943         PetscInt bOff, q, allDof = 0;
6944 
6945         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6946         for (q = 0; q < bDof; q++) {
6947           PetscInt a = anchors[bOff + q], aDof;
6948 
6949           PetscCall(PetscSectionGetDof(section, a, &aDof));
6950           allDof += aDof;
6951         }
6952         newPointOffsets[0][p+1] = allDof;
6953         pointMatOffsets[0][p+1] = bSecDof * allDof;
6954       }
6955       else {
6956         newPointOffsets[0][p+1] = bSecDof;
6957         pointMatOffsets[0][p+1] = 0;
6958       }
6959     }
6960     newPointOffsets[0][0] = 0;
6961     pointMatOffsets[0][0] = 0;
6962     for (p = 0; p < numPoints; p++) {
6963       newPointOffsets[0][p+1] += newPointOffsets[0][p];
6964       pointMatOffsets[0][p+1] += pointMatOffsets[0][p];
6965     }
6966     PetscCall(DMGetWorkArray(dm,pointMatOffsets[0][numPoints],MPIU_SCALAR,&pointMat[0]));
6967   }
6968 
6969   /* output arrays */
6970   PetscCall(DMGetWorkArray(dm,2*newNumPoints,MPIU_INT,&newPoints));
6971 
6972   /* get the point-to-point matrices; construct newPoints */
6973   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
6974   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
6975   PetscCall(DMGetWorkArray(dm,maxDof,MPIU_INT,&indices));
6976   PetscCall(DMGetWorkArray(dm,maxAnchor*maxDof,MPIU_INT,&newIndices));
6977   if (numFields) {
6978     for (p = 0, newP = 0; p < numPoints; p++) {
6979       PetscInt b    = points[2*p];
6980       PetscInt o    = points[2*p+1];
6981       PetscInt bDof = 0, bSecDof;
6982 
6983       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
6984       if (!bSecDof) {
6985         continue;
6986       }
6987       if (b >= aStart && b < aEnd) {
6988         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6989       }
6990       if (bDof) {
6991         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
6992 
6993         fStart[0] = 0;
6994         fEnd[0]   = 0;
6995         for (f = 0; f < numFields; f++) {
6996           PetscInt fDof;
6997 
6998           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
6999           fStart[f+1] = fStart[f] + fDof;
7000           fEnd[f+1]   = fStart[f+1];
7001         }
7002         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7003         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7004 
7005         fAnchorStart[0] = 0;
7006         fAnchorEnd[0]   = 0;
7007         for (f = 0; f < numFields; f++) {
7008           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7009 
7010           fAnchorStart[f+1] = fAnchorStart[f] + fDof;
7011           fAnchorEnd[f+1]   = fAnchorStart[f + 1];
7012         }
7013         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7014         for (q = 0; q < bDof; q++) {
7015           PetscInt a = anchors[bOff + q], aOff;
7016 
7017           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7018           newPoints[2*(newP + q)]     = a;
7019           newPoints[2*(newP + q) + 1] = 0;
7020           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7021           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7022         }
7023         newP += bDof;
7024 
7025         if (outValues) {
7026           /* get the point-to-point submatrix */
7027           for (f = 0; f < numFields; f++) {
7028             PetscCall(MatGetValues(cMat,fEnd[f]-fStart[f],indices + fStart[f],fAnchorEnd[f] - fAnchorStart[f],newIndices + fAnchorStart[f],pointMat[f] + pointMatOffsets[f][p]));
7029           }
7030         }
7031       }
7032       else {
7033         newPoints[2 * newP]     = b;
7034         newPoints[2 * newP + 1] = o;
7035         newP++;
7036       }
7037     }
7038   } else {
7039     for (p = 0; p < numPoints; p++) {
7040       PetscInt b    = points[2*p];
7041       PetscInt o    = points[2*p+1];
7042       PetscInt bDof = 0, bSecDof;
7043 
7044       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7045       if (!bSecDof) {
7046         continue;
7047       }
7048       if (b >= aStart && b < aEnd) {
7049         PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7050       }
7051       if (bDof) {
7052         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7053 
7054         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7055         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7056 
7057         PetscCall(PetscSectionGetOffset (aSec, b, &bOff));
7058         for (q = 0; q < bDof; q++) {
7059           PetscInt a = anchors[bOff + q], aOff;
7060 
7061           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7062 
7063           newPoints[2*(newP + q)]     = a;
7064           newPoints[2*(newP + q) + 1] = 0;
7065           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7066           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7067         }
7068         newP += bDof;
7069 
7070         /* get the point-to-point submatrix */
7071         if (outValues) {
7072           PetscCall(MatGetValues(cMat,bEnd,indices,bAnchorEnd,newIndices,pointMat[0] + pointMatOffsets[0][p]));
7073         }
7074       }
7075       else {
7076         newPoints[2 * newP]     = b;
7077         newPoints[2 * newP + 1] = o;
7078         newP++;
7079       }
7080     }
7081   }
7082 
7083   if (outValues) {
7084     PetscCall(DMGetWorkArray(dm,newNumIndices*numIndices,MPIU_SCALAR,&tmpValues));
7085     PetscCall(PetscArrayzero(tmpValues,newNumIndices*numIndices));
7086     /* multiply constraints on the right */
7087     if (numFields) {
7088       for (f = 0; f < numFields; f++) {
7089         PetscInt oldOff = offsets[f];
7090 
7091         for (p = 0; p < numPoints; p++) {
7092           PetscInt cStart = newPointOffsets[f][p];
7093           PetscInt b      = points[2 * p];
7094           PetscInt c, r, k;
7095           PetscInt dof;
7096 
7097           PetscCall(PetscSectionGetFieldDof(section,b,f,&dof));
7098           if (!dof) {
7099             continue;
7100           }
7101           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7102             PetscInt nCols         = newPointOffsets[f][p+1]-cStart;
7103             const PetscScalar *mat = pointMat[f] + pointMatOffsets[f][p];
7104 
7105             for (r = 0; r < numIndices; r++) {
7106               for (c = 0; c < nCols; c++) {
7107                 for (k = 0; k < dof; k++) {
7108                   tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7109                 }
7110               }
7111             }
7112           }
7113           else {
7114             /* copy this column as is */
7115             for (r = 0; r < numIndices; r++) {
7116               for (c = 0; c < dof; c++) {
7117                 tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7118               }
7119             }
7120           }
7121           oldOff += dof;
7122         }
7123       }
7124     }
7125     else {
7126       PetscInt oldOff = 0;
7127       for (p = 0; p < numPoints; p++) {
7128         PetscInt cStart = newPointOffsets[0][p];
7129         PetscInt b      = points[2 * p];
7130         PetscInt c, r, k;
7131         PetscInt dof;
7132 
7133         PetscCall(PetscSectionGetDof(section,b,&dof));
7134         if (!dof) {
7135           continue;
7136         }
7137         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7138           PetscInt nCols         = newPointOffsets[0][p+1]-cStart;
7139           const PetscScalar *mat = pointMat[0] + pointMatOffsets[0][p];
7140 
7141           for (r = 0; r < numIndices; r++) {
7142             for (c = 0; c < nCols; c++) {
7143               for (k = 0; k < dof; k++) {
7144                 tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7145               }
7146             }
7147           }
7148         }
7149         else {
7150           /* copy this column as is */
7151           for (r = 0; r < numIndices; r++) {
7152             for (c = 0; c < dof; c++) {
7153               tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7154             }
7155           }
7156         }
7157         oldOff += dof;
7158       }
7159     }
7160 
7161     if (multiplyLeft) {
7162       PetscCall(DMGetWorkArray(dm,newNumIndices*newNumIndices,MPIU_SCALAR,&newValues));
7163       PetscCall(PetscArrayzero(newValues,newNumIndices*newNumIndices));
7164       /* multiply constraints transpose on the left */
7165       if (numFields) {
7166         for (f = 0; f < numFields; f++) {
7167           PetscInt oldOff = offsets[f];
7168 
7169           for (p = 0; p < numPoints; p++) {
7170             PetscInt rStart = newPointOffsets[f][p];
7171             PetscInt b      = points[2 * p];
7172             PetscInt c, r, k;
7173             PetscInt dof;
7174 
7175             PetscCall(PetscSectionGetFieldDof(section,b,f,&dof));
7176             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7177               PetscInt nRows                        = newPointOffsets[f][p+1]-rStart;
7178               const PetscScalar *PETSC_RESTRICT mat = pointMat[f] + pointMatOffsets[f][p];
7179 
7180               for (r = 0; r < nRows; r++) {
7181                 for (c = 0; c < newNumIndices; c++) {
7182                   for (k = 0; k < dof; k++) {
7183                     newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7184                   }
7185                 }
7186               }
7187             }
7188             else {
7189               /* copy this row as is */
7190               for (r = 0; r < dof; r++) {
7191                 for (c = 0; c < newNumIndices; c++) {
7192                   newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7193                 }
7194               }
7195             }
7196             oldOff += dof;
7197           }
7198         }
7199       }
7200       else {
7201         PetscInt oldOff = 0;
7202 
7203         for (p = 0; p < numPoints; p++) {
7204           PetscInt rStart = newPointOffsets[0][p];
7205           PetscInt b      = points[2 * p];
7206           PetscInt c, r, k;
7207           PetscInt dof;
7208 
7209           PetscCall(PetscSectionGetDof(section,b,&dof));
7210           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7211             PetscInt nRows                        = newPointOffsets[0][p+1]-rStart;
7212             const PetscScalar *PETSC_RESTRICT mat = pointMat[0] + pointMatOffsets[0][p];
7213 
7214             for (r = 0; r < nRows; r++) {
7215               for (c = 0; c < newNumIndices; c++) {
7216                 for (k = 0; k < dof; k++) {
7217                   newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7218                 }
7219               }
7220             }
7221           }
7222           else {
7223             /* copy this row as is */
7224             for (r = 0; r < dof; r++) {
7225               for (c = 0; c < newNumIndices; c++) {
7226                 newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7227               }
7228             }
7229           }
7230           oldOff += dof;
7231         }
7232       }
7233 
7234       PetscCall(DMRestoreWorkArray(dm,newNumIndices*numIndices,MPIU_SCALAR,&tmpValues));
7235     }
7236     else {
7237       newValues = tmpValues;
7238     }
7239   }
7240 
7241   /* clean up */
7242   PetscCall(DMRestoreWorkArray(dm,maxDof,MPIU_INT,&indices));
7243   PetscCall(DMRestoreWorkArray(dm,maxAnchor*maxDof,MPIU_INT,&newIndices));
7244 
7245   if (numFields) {
7246     for (f = 0; f < numFields; f++) {
7247       PetscCall(DMRestoreWorkArray(dm,pointMatOffsets[f][numPoints],MPIU_SCALAR,&pointMat[f]));
7248       PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[f]));
7249       PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[f]));
7250     }
7251   }
7252   else {
7253     PetscCall(DMRestoreWorkArray(dm,pointMatOffsets[0][numPoints],MPIU_SCALAR,&pointMat[0]));
7254     PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&pointMatOffsets[0]));
7255     PetscCall(DMRestoreWorkArray(dm,numPoints+1,MPIU_INT,&newPointOffsets[0]));
7256   }
7257   PetscCall(ISRestoreIndices(aIS,&anchors));
7258 
7259   /* output */
7260   if (outPoints) {
7261     *outPoints = newPoints;
7262   }
7263   else {
7264     PetscCall(DMRestoreWorkArray(dm,2*newNumPoints,MPIU_INT,&newPoints));
7265   }
7266   if (outValues) {
7267     *outValues = newValues;
7268   }
7269   for (f = 0; f <= numFields; f++) {
7270     offsets[f] = newOffsets[f];
7271   }
7272   PetscFunctionReturn(0);
7273 }
7274 
7275 /*@C
7276   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7277 
7278   Not collective
7279 
7280   Input Parameters:
7281 + dm         - The DM
7282 . section    - The PetscSection describing the points (a local section)
7283 . idxSection - The PetscSection from which to obtain indices (may be local or global)
7284 . point      - The point defining the closure
7285 - useClPerm  - Use the closure point permutation if available
7286 
7287   Output Parameters:
7288 + numIndices - The number of dof indices in the closure of point with the input sections
7289 . indices    - The dof indices
7290 . outOffsets - Array to write the field offsets into, or NULL
7291 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7292 
7293   Notes:
7294   Must call DMPlexRestoreClosureIndices() to free allocated memory
7295 
7296   If idxSection is global, any constrained dofs (see DMAddBoundary(), for example) will get negative indices.  The value
7297   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7298   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7299   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7300   indices (with the above semantics) are implied.
7301 
7302   Level: advanced
7303 
7304 .seealso `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7305 @*/
7306 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm,
7307                                        PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7308 {
7309   /* Closure ordering */
7310   PetscSection        clSection;
7311   IS                  clPoints;
7312   const PetscInt     *clp;
7313   PetscInt           *points;
7314   const PetscInt     *clperm = NULL;
7315   /* Dof permutation and sign flips */
7316   const PetscInt    **perms[32] = {NULL};
7317   const PetscScalar **flips[32] = {NULL};
7318   PetscScalar        *valCopy   = NULL;
7319   /* Hanging node constraints */
7320   PetscInt           *pointsC = NULL;
7321   PetscScalar        *valuesC = NULL;
7322   PetscInt            NclC, NiC;
7323 
7324   PetscInt           *idx;
7325   PetscInt            Nf, Ncl, Ni = 0, offsets[32], p, f;
7326   PetscBool           isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7327 
7328   PetscFunctionBeginHot;
7329   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7330   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7331   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7332   if (numIndices) PetscValidIntPointer(numIndices, 6);
7333   if (indices)    PetscValidPointer(indices, 7);
7334   if (outOffsets) PetscValidIntPointer(outOffsets, 8);
7335   if (values)     PetscValidPointer(values, 9);
7336   PetscCall(PetscSectionGetNumFields(section, &Nf));
7337   PetscCheck(Nf <= 31,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7338   PetscCall(PetscArrayzero(offsets, 32));
7339   /* 1) Get points in closure */
7340   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7341   if (useClPerm) {
7342     PetscInt depth, clsize;
7343     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7344     for (clsize=0,p=0; p<Ncl; p++) {
7345       PetscInt dof;
7346       PetscCall(PetscSectionGetDof(section, points[2*p], &dof));
7347       clsize += dof;
7348     }
7349     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject) dm, depth, clsize, &clperm));
7350   }
7351   /* 2) Get number of indices on these points and field offsets from section */
7352   for (p = 0; p < Ncl*2; p += 2) {
7353     PetscInt dof, fdof;
7354 
7355     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7356     for (f = 0; f < Nf; ++f) {
7357       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7358       offsets[f+1] += fdof;
7359     }
7360     Ni += dof;
7361   }
7362   for (f = 1; f < Nf; ++f) offsets[f+1] += offsets[f];
7363   PetscCheck(!Nf || offsets[Nf] == Ni,PetscObjectComm((PetscObject) dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7364   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7365   for (f = 0; f < PetscMax(1, Nf); ++f) {
7366     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7367     else    PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7368     /* may need to apply sign changes to the element matrix */
7369     if (values && flips[f]) {
7370       PetscInt foffset = offsets[f];
7371 
7372       for (p = 0; p < Ncl; ++p) {
7373         PetscInt           pnt  = points[2*p], fdof;
7374         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7375 
7376         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7377         else     PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7378         if (flip) {
7379           PetscInt i, j, k;
7380 
7381           if (!valCopy) {
7382             PetscCall(DMGetWorkArray(dm, Ni*Ni, MPIU_SCALAR, &valCopy));
7383             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7384             *values = valCopy;
7385           }
7386           for (i = 0; i < fdof; ++i) {
7387             PetscScalar fval = flip[i];
7388 
7389             for (k = 0; k < Ni; ++k) {
7390               valCopy[Ni * (foffset + i) + k] *= fval;
7391               valCopy[Ni * k + (foffset + i)] *= fval;
7392             }
7393           }
7394         }
7395         foffset += fdof;
7396       }
7397     }
7398   }
7399   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7400   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7401   if (NclC) {
7402     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni*Ni, MPIU_SCALAR, &valCopy));
7403     for (f = 0; f < PetscMax(1, Nf); ++f) {
7404       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7405       else    PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7406     }
7407     for (f = 0; f < PetscMax(1, Nf); ++f) {
7408       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7409       else    PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7410     }
7411     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7412     Ncl     = NclC;
7413     Ni      = NiC;
7414     points  = pointsC;
7415     if (values) *values = valuesC;
7416   }
7417   /* 5) Calculate indices */
7418   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7419   if (Nf) {
7420     PetscInt  idxOff;
7421     PetscBool useFieldOffsets;
7422 
7423     if (outOffsets) {for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];}
7424     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7425     if (useFieldOffsets) {
7426       for (p = 0; p < Ncl; ++p) {
7427         const PetscInt pnt = points[p*2];
7428 
7429         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7430       }
7431     } else {
7432       for (p = 0; p < Ncl; ++p) {
7433         const PetscInt pnt = points[p*2];
7434 
7435         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7436         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7437          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7438          * global section. */
7439         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff+1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7440       }
7441     }
7442   } else {
7443     PetscInt off = 0, idxOff;
7444 
7445     for (p = 0; p < Ncl; ++p) {
7446       const PetscInt  pnt  = points[p*2];
7447       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7448 
7449       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7450       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7451        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7452       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff+1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7453     }
7454   }
7455   /* 6) Cleanup */
7456   for (f = 0; f < PetscMax(1, Nf); ++f) {
7457     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7458     else    PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7459   }
7460   if (NclC) {
7461     PetscCall(DMRestoreWorkArray(dm, NclC*2, MPIU_INT, &pointsC));
7462   } else {
7463     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7464   }
7465 
7466   if (numIndices) *numIndices = Ni;
7467   if (indices)    *indices    = idx;
7468   PetscFunctionReturn(0);
7469 }
7470 
7471 /*@C
7472   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7473 
7474   Not collective
7475 
7476   Input Parameters:
7477 + dm         - The DM
7478 . section    - The PetscSection describing the points (a local section)
7479 . idxSection - The PetscSection from which to obtain indices (may be local or global)
7480 . point      - The point defining the closure
7481 - useClPerm  - Use the closure point permutation if available
7482 
7483   Output Parameters:
7484 + numIndices - The number of dof indices in the closure of point with the input sections
7485 . indices    - The dof indices
7486 . outOffsets - Array to write the field offsets into, or NULL
7487 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7488 
7489   Notes:
7490   If values were modified, the user is responsible for calling DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values).
7491 
7492   If idxSection is global, any constrained dofs (see DMAddBoundary(), for example) will get negative indices.  The value
7493   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7494   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7495   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7496   indices (with the above semantics) are implied.
7497 
7498   Level: advanced
7499 
7500 .seealso `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7501 @*/
7502 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm,
7503                                            PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7504 {
7505   PetscFunctionBegin;
7506   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7507   PetscValidPointer(indices, 7);
7508   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
7509   PetscFunctionReturn(0);
7510 }
7511 
7512 /*@C
7513   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
7514 
7515   Not collective
7516 
7517   Input Parameters:
7518 + dm - The DM
7519 . section - The section describing the layout in v, or NULL to use the default section
7520 . globalSection - The section describing the layout in v, or NULL to use the default global section
7521 . A - The matrix
7522 . point - The point in the DM
7523 . values - The array of values
7524 - mode - The insert mode, where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions
7525 
7526   Fortran Notes:
7527   This routine is only available in Fortran 90, and you must include petsc.h90 in your code.
7528 
7529   Level: intermediate
7530 
7531 .seealso `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7532 @*/
7533 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7534 {
7535   DM_Plex           *mesh = (DM_Plex*) dm->data;
7536   PetscInt          *indices;
7537   PetscInt           numIndices;
7538   const PetscScalar *valuesOrig = values;
7539   PetscErrorCode     ierr;
7540 
7541   PetscFunctionBegin;
7542   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7543   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7544   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7545   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
7546   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
7547   PetscValidHeaderSpecific(A, MAT_CLASSID, 4);
7548 
7549   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **) &values));
7550 
7551   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
7552   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7553   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
7554   if (ierr) {
7555     PetscMPIInt    rank;
7556 
7557     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7558     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7559     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
7560     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **) &values));
7561     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7562     SETERRQ(PetscObjectComm((PetscObject)dm),ierr,"Not possible to set matrix values");
7563   }
7564   if (mesh->printFEM > 1) {
7565     PetscInt i;
7566     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
7567     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
7568     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
7569   }
7570 
7571   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **) &values));
7572   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7573   PetscFunctionReturn(0);
7574 }
7575 
7576 /*@C
7577   DMPlexMatSetClosure - Set an array of the values on the closure of 'point' using a different row and column section
7578 
7579   Not collective
7580 
7581   Input Parameters:
7582 + dmRow - The DM for the row fields
7583 . sectionRow - The section describing the layout, or NULL to use the default section in dmRow
7584 . globalSectionRow - The section describing the layout, or NULL to use the default global section in dmRow
7585 . dmCol - The DM for the column fields
7586 . sectionCol - The section describing the layout, or NULL to use the default section in dmCol
7587 . globalSectionCol - The section describing the layout, or NULL to use the default global section in dmCol
7588 . A - The matrix
7589 . point - The point in the DMs
7590 . values - The array of values
7591 - mode - The insert mode, where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions
7592 
7593   Level: intermediate
7594 
7595 .seealso `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7596 @*/
7597 PetscErrorCode DMPlexMatSetClosureGeneral(DM dmRow, PetscSection sectionRow, PetscSection globalSectionRow, DM dmCol, PetscSection sectionCol, PetscSection globalSectionCol, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7598 {
7599   DM_Plex           *mesh = (DM_Plex*) dmRow->data;
7600   PetscInt          *indicesRow, *indicesCol;
7601   PetscInt           numIndicesRow, numIndicesCol;
7602   const PetscScalar *valuesOrig = values;
7603   PetscErrorCode     ierr;
7604 
7605   PetscFunctionBegin;
7606   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
7607   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
7608   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
7609   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
7610   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
7611   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 4);
7612   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
7613   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 5);
7614   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
7615   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 6);
7616   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7617 
7618   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values));
7619   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **) &values));
7620 
7621   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7622   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7623   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
7624   if (ierr) {
7625     PetscMPIInt    rank;
7626 
7627     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7628     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7629     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7630     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values));
7631     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **) &values));
7632     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7633   }
7634 
7635   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **) &values));
7636   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **) &values));
7637   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7638   PetscFunctionReturn(0);
7639 }
7640 
7641 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7642 {
7643   DM_Plex        *mesh   = (DM_Plex*) dmf->data;
7644   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7645   PetscInt       *cpoints = NULL;
7646   PetscInt       *findices, *cindices;
7647   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7648   PetscInt        foffsets[32], coffsets[32];
7649   DMPolytopeType  ct;
7650   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7651   PetscErrorCode  ierr;
7652 
7653   PetscFunctionBegin;
7654   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7655   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7656   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7657   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7658   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7659   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7660   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7661   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7662   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7663   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7664   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7665   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7666   PetscCheck(numFields <= 31,PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7667   PetscCall(PetscArrayzero(foffsets, 32));
7668   PetscCall(PetscArrayzero(coffsets, 32));
7669   /* Column indices */
7670   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7671   maxFPoints = numCPoints;
7672   /* Compress out points not in the section */
7673   /*   TODO: Squeeze out points with 0 dof as well */
7674   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7675   for (p = 0, q = 0; p < numCPoints*2; p += 2) {
7676     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7677       cpoints[q*2]   = cpoints[p];
7678       cpoints[q*2+1] = cpoints[p+1];
7679       ++q;
7680     }
7681   }
7682   numCPoints = q;
7683   for (p = 0, numCIndices = 0; p < numCPoints*2; p += 2) {
7684     PetscInt fdof;
7685 
7686     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7687     if (!dof) continue;
7688     for (f = 0; f < numFields; ++f) {
7689       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7690       coffsets[f+1] += fdof;
7691     }
7692     numCIndices += dof;
7693   }
7694   for (f = 1; f < numFields; ++f) coffsets[f+1] += coffsets[f];
7695   /* Row indices */
7696   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7697   {
7698     DMPlexTransform tr;
7699     DMPolytopeType *rct;
7700     PetscInt       *rsize, *rcone, *rornt, Nt;
7701 
7702     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7703     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7704     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7705     numSubcells = rsize[Nt-1];
7706     PetscCall(DMPlexTransformDestroy(&tr));
7707   }
7708   PetscCall(DMGetWorkArray(dmf, maxFPoints*2*numSubcells, MPIU_INT, &ftotpoints));
7709   for (r = 0, q = 0; r < numSubcells; ++r) {
7710     /* TODO Map from coarse to fine cells */
7711     PetscCall(DMPlexGetTransitiveClosure(dmf, point*numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7712     /* Compress out points not in the section */
7713     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7714     for (p = 0; p < numFPoints*2; p += 2) {
7715       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7716         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7717         if (!dof) continue;
7718         for (s = 0; s < q; ++s) if (fpoints[p] == ftotpoints[s*2]) break;
7719         if (s < q) continue;
7720         ftotpoints[q*2]   = fpoints[p];
7721         ftotpoints[q*2+1] = fpoints[p+1];
7722         ++q;
7723       }
7724     }
7725     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7726   }
7727   numFPoints = q;
7728   for (p = 0, numFIndices = 0; p < numFPoints*2; p += 2) {
7729     PetscInt fdof;
7730 
7731     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
7732     if (!dof) continue;
7733     for (f = 0; f < numFields; ++f) {
7734       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
7735       foffsets[f+1] += fdof;
7736     }
7737     numFIndices += dof;
7738   }
7739   for (f = 1; f < numFields; ++f) foffsets[f+1] += foffsets[f];
7740 
7741   PetscCheck(!numFields || foffsets[numFields] == numFIndices,PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
7742   PetscCheck(!numFields || coffsets[numFields] == numCIndices,PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
7743   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7744   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7745   if (numFields) {
7746     const PetscInt **permsF[32] = {NULL};
7747     const PetscInt **permsC[32] = {NULL};
7748 
7749     for (f = 0; f < numFields; f++) {
7750       PetscCall(PetscSectionGetFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7751       PetscCall(PetscSectionGetFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7752     }
7753     for (p = 0; p < numFPoints; p++) {
7754       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7755       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
7756     }
7757     for (p = 0; p < numCPoints; p++) {
7758       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7759       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
7760     }
7761     for (f = 0; f < numFields; f++) {
7762       PetscCall(PetscSectionRestoreFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7763       PetscCall(PetscSectionRestoreFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7764     }
7765   } else {
7766     const PetscInt **permsF = NULL;
7767     const PetscInt **permsC = NULL;
7768 
7769     PetscCall(PetscSectionGetPointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7770     PetscCall(PetscSectionGetPointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7771     for (p = 0, off = 0; p < numFPoints; p++) {
7772       const PetscInt *perm = permsF ? permsF[p] : NULL;
7773 
7774       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7775       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
7776     }
7777     for (p = 0, off = 0; p < numCPoints; p++) {
7778       const PetscInt *perm = permsC ? permsC[p] : NULL;
7779 
7780       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7781       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
7782     }
7783     PetscCall(PetscSectionRestorePointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7784     PetscCall(PetscSectionRestorePointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7785   }
7786   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
7787   /* TODO: flips */
7788   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7789   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
7790   if (ierr) {
7791     PetscMPIInt    rank;
7792 
7793     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7794     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7795     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
7796     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7797     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7798   }
7799   PetscCall(DMRestoreWorkArray(dmf, numCPoints*2*4, MPIU_INT, &ftotpoints));
7800   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7801   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7802   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7803   PetscFunctionReturn(0);
7804 }
7805 
7806 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
7807 {
7808   PetscInt      *fpoints = NULL, *ftotpoints = NULL;
7809   PetscInt      *cpoints = NULL;
7810   PetscInt       foffsets[32], coffsets[32];
7811   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7812   DMPolytopeType ct;
7813   PetscInt       numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7814 
7815   PetscFunctionBegin;
7816   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7817   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7818   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7819   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7820   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7821   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7822   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7823   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7824   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7825   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7826   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7827   PetscCheck(numFields <= 31,PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7828   PetscCall(PetscArrayzero(foffsets, 32));
7829   PetscCall(PetscArrayzero(coffsets, 32));
7830   /* Column indices */
7831   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7832   maxFPoints = numCPoints;
7833   /* Compress out points not in the section */
7834   /*   TODO: Squeeze out points with 0 dof as well */
7835   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7836   for (p = 0, q = 0; p < numCPoints*2; p += 2) {
7837     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7838       cpoints[q*2]   = cpoints[p];
7839       cpoints[q*2+1] = cpoints[p+1];
7840       ++q;
7841     }
7842   }
7843   numCPoints = q;
7844   for (p = 0, numCIndices = 0; p < numCPoints*2; p += 2) {
7845     PetscInt fdof;
7846 
7847     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7848     if (!dof) continue;
7849     for (f = 0; f < numFields; ++f) {
7850       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7851       coffsets[f+1] += fdof;
7852     }
7853     numCIndices += dof;
7854   }
7855   for (f = 1; f < numFields; ++f) coffsets[f+1] += coffsets[f];
7856   /* Row indices */
7857   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7858   {
7859     DMPlexTransform tr;
7860     DMPolytopeType *rct;
7861     PetscInt       *rsize, *rcone, *rornt, Nt;
7862 
7863     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7864     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7865     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7866     numSubcells = rsize[Nt-1];
7867     PetscCall(DMPlexTransformDestroy(&tr));
7868   }
7869   PetscCall(DMGetWorkArray(dmf, maxFPoints*2*numSubcells, MPIU_INT, &ftotpoints));
7870   for (r = 0, q = 0; r < numSubcells; ++r) {
7871     /* TODO Map from coarse to fine cells */
7872     PetscCall(DMPlexGetTransitiveClosure(dmf, point*numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7873     /* Compress out points not in the section */
7874     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7875     for (p = 0; p < numFPoints*2; p += 2) {
7876       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7877         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7878         if (!dof) continue;
7879         for (s = 0; s < q; ++s) if (fpoints[p] == ftotpoints[s*2]) break;
7880         if (s < q) continue;
7881         ftotpoints[q*2]   = fpoints[p];
7882         ftotpoints[q*2+1] = fpoints[p+1];
7883         ++q;
7884       }
7885     }
7886     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7887   }
7888   numFPoints = q;
7889   for (p = 0, numFIndices = 0; p < numFPoints*2; p += 2) {
7890     PetscInt fdof;
7891 
7892     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
7893     if (!dof) continue;
7894     for (f = 0; f < numFields; ++f) {
7895       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
7896       foffsets[f+1] += fdof;
7897     }
7898     numFIndices += dof;
7899   }
7900   for (f = 1; f < numFields; ++f) foffsets[f+1] += foffsets[f];
7901 
7902   PetscCheck(!numFields || foffsets[numFields] == numFIndices,PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
7903   PetscCheck(!numFields || coffsets[numFields] == numCIndices,PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
7904   if (numFields) {
7905     const PetscInt **permsF[32] = {NULL};
7906     const PetscInt **permsC[32] = {NULL};
7907 
7908     for (f = 0; f < numFields; f++) {
7909       PetscCall(PetscSectionGetFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7910       PetscCall(PetscSectionGetFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7911     }
7912     for (p = 0; p < numFPoints; p++) {
7913       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7914       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
7915     }
7916     for (p = 0; p < numCPoints; p++) {
7917       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7918       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
7919     }
7920     for (f = 0; f < numFields; f++) {
7921       PetscCall(PetscSectionRestoreFieldPointSyms(fsection,f,numFPoints,ftotpoints,&permsF[f],NULL));
7922       PetscCall(PetscSectionRestoreFieldPointSyms(csection,f,numCPoints,cpoints,&permsC[f],NULL));
7923     }
7924   } else {
7925     const PetscInt **permsF = NULL;
7926     const PetscInt **permsC = NULL;
7927 
7928     PetscCall(PetscSectionGetPointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7929     PetscCall(PetscSectionGetPointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7930     for (p = 0, off = 0; p < numFPoints; p++) {
7931       const PetscInt *perm = permsF ? permsF[p] : NULL;
7932 
7933       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2*p], &globalOff));
7934       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
7935     }
7936     for (p = 0, off = 0; p < numCPoints; p++) {
7937       const PetscInt *perm = permsC ? permsC[p] : NULL;
7938 
7939       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2*p], &globalOff));
7940       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2*p], globalOff < 0 ? -(globalOff+1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
7941     }
7942     PetscCall(PetscSectionRestorePointSyms(fsection,numFPoints,ftotpoints,&permsF,NULL));
7943     PetscCall(PetscSectionRestorePointSyms(csection,numCPoints,cpoints,&permsC,NULL));
7944   }
7945   PetscCall(DMRestoreWorkArray(dmf, numCPoints*2*4, MPIU_INT, &ftotpoints));
7946   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7947   PetscFunctionReturn(0);
7948 }
7949 
7950 /*@C
7951   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
7952 
7953   Input Parameter:
7954 . dm   - The DMPlex object
7955 
7956   Output Parameter:
7957 . cellHeight - The height of a cell
7958 
7959   Level: developer
7960 
7961 .seealso `DMPlexSetVTKCellHeight()`
7962 @*/
7963 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
7964 {
7965   DM_Plex *mesh = (DM_Plex*) dm->data;
7966 
7967   PetscFunctionBegin;
7968   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7969   PetscValidIntPointer(cellHeight, 2);
7970   *cellHeight = mesh->vtkCellHeight;
7971   PetscFunctionReturn(0);
7972 }
7973 
7974 /*@C
7975   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
7976 
7977   Input Parameters:
7978 + dm   - The DMPlex object
7979 - cellHeight - The height of a cell
7980 
7981   Level: developer
7982 
7983 .seealso `DMPlexGetVTKCellHeight()`
7984 @*/
7985 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
7986 {
7987   DM_Plex *mesh = (DM_Plex*) dm->data;
7988 
7989   PetscFunctionBegin;
7990   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7991   mesh->vtkCellHeight = cellHeight;
7992   PetscFunctionReturn(0);
7993 }
7994 
7995 /*@
7996   DMPlexGetGhostCellStratum - Get the range of cells which are used to enforce FV boundary conditions
7997 
7998   Input Parameter:
7999 . dm - The DMPlex object
8000 
8001   Output Parameters:
8002 + gcStart - The first ghost cell, or NULL
8003 - gcEnd   - The upper bound on ghost cells, or NULL
8004 
8005   Level: advanced
8006 
8007 .seealso `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
8008 @*/
8009 PetscErrorCode DMPlexGetGhostCellStratum(DM dm, PetscInt *gcStart, PetscInt *gcEnd)
8010 {
8011   DMLabel        ctLabel;
8012 
8013   PetscFunctionBegin;
8014   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8015   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
8016   PetscCall(DMLabelGetStratumBounds(ctLabel, DM_POLYTOPE_FV_GHOST, gcStart, gcEnd));
8017   PetscFunctionReturn(0);
8018 }
8019 
8020 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8021 {
8022   PetscSection   section, globalSection;
8023   PetscInt      *numbers, p;
8024 
8025   PetscFunctionBegin;
8026   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf));
8027   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8028   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8029   for (p = pStart; p < pEnd; ++p) {
8030     PetscCall(PetscSectionSetDof(section, p, 1));
8031   }
8032   PetscCall(PetscSectionSetUp(section));
8033   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8034   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8035   for (p = pStart; p < pEnd; ++p) {
8036     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p-pStart]));
8037     if (numbers[p-pStart] < 0) numbers[p-pStart] -= shift;
8038     else                       numbers[p-pStart] += shift;
8039   }
8040   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject) dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8041   if (globalSize) {
8042     PetscLayout layout;
8043     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject) dm), globalSection, &layout));
8044     PetscCall(PetscLayoutGetSize(layout, globalSize));
8045     PetscCall(PetscLayoutDestroy(&layout));
8046   }
8047   PetscCall(PetscSectionDestroy(&section));
8048   PetscCall(PetscSectionDestroy(&globalSection));
8049   PetscFunctionReturn(0);
8050 }
8051 
8052 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8053 {
8054   PetscInt       cellHeight, cStart, cEnd;
8055 
8056   PetscFunctionBegin;
8057   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8058   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8059   else               PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8060   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8061   PetscFunctionReturn(0);
8062 }
8063 
8064 /*@
8065   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8066 
8067   Input Parameter:
8068 . dm   - The DMPlex object
8069 
8070   Output Parameter:
8071 . globalCellNumbers - Global cell numbers for all cells on this process
8072 
8073   Level: developer
8074 
8075 .seealso `DMPlexGetVertexNumbering()`
8076 @*/
8077 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8078 {
8079   DM_Plex       *mesh = (DM_Plex*) dm->data;
8080 
8081   PetscFunctionBegin;
8082   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8083   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8084   *globalCellNumbers = mesh->globalCellNumbers;
8085   PetscFunctionReturn(0);
8086 }
8087 
8088 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8089 {
8090   PetscInt       vStart, vEnd;
8091 
8092   PetscFunctionBegin;
8093   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8094   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8095   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8096   PetscFunctionReturn(0);
8097 }
8098 
8099 /*@
8100   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8101 
8102   Input Parameter:
8103 . dm   - The DMPlex object
8104 
8105   Output Parameter:
8106 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8107 
8108   Level: developer
8109 
8110 .seealso `DMPlexGetCellNumbering()`
8111 @*/
8112 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8113 {
8114   DM_Plex       *mesh = (DM_Plex*) dm->data;
8115 
8116   PetscFunctionBegin;
8117   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8118   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8119   *globalVertexNumbers = mesh->globalVertexNumbers;
8120   PetscFunctionReturn(0);
8121 }
8122 
8123 /*@
8124   DMPlexCreatePointNumbering - Create a global numbering for all points on this process
8125 
8126   Input Parameter:
8127 . dm   - The DMPlex object
8128 
8129   Output Parameter:
8130 . globalPointNumbers - Global numbers for all points on this process
8131 
8132   Level: developer
8133 
8134 .seealso `DMPlexGetCellNumbering()`
8135 @*/
8136 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8137 {
8138   IS             nums[4];
8139   PetscInt       depths[4], gdepths[4], starts[4];
8140   PetscInt       depth, d, shift = 0;
8141 
8142   PetscFunctionBegin;
8143   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8144   PetscCall(DMPlexGetDepth(dm, &depth));
8145   /* For unstratified meshes use dim instead of depth */
8146   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8147   for (d = 0; d <= depth; ++d) {
8148     PetscInt end;
8149 
8150     depths[d] = depth-d;
8151     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8152     if (!(starts[d]-end)) { starts[d] = depths[d] = -1; }
8153   }
8154   PetscCall(PetscSortIntWithArray(depth+1, starts, depths));
8155   PetscCall(MPIU_Allreduce(depths, gdepths, depth+1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject) dm)));
8156   for (d = 0; d <= depth; ++d) {
8157     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]);
8158   }
8159   for (d = 0; d <= depth; ++d) {
8160     PetscInt pStart, pEnd, gsize;
8161 
8162     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8163     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8164     shift += gsize;
8165   }
8166   PetscCall(ISConcatenate(PetscObjectComm((PetscObject) dm), depth+1, nums, globalPointNumbers));
8167   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8168   PetscFunctionReturn(0);
8169 }
8170 
8171 /*@
8172   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8173 
8174   Input Parameter:
8175 . dm - The DMPlex object
8176 
8177   Output Parameter:
8178 . ranks - The rank field
8179 
8180   Options Database Keys:
8181 . -dm_partition_view - Adds the rank field into the DM output from -dm_view using the same viewer
8182 
8183   Level: intermediate
8184 
8185 .seealso: `DMView()`
8186 @*/
8187 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8188 {
8189   DM             rdm;
8190   PetscFE        fe;
8191   PetscScalar   *r;
8192   PetscMPIInt    rank;
8193   DMPolytopeType ct;
8194   PetscInt       dim, cStart, cEnd, c;
8195   PetscBool      simplex;
8196 
8197   PetscFunctionBeginUser;
8198   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8199   PetscValidPointer(ranks, 2);
8200   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject) dm), &rank));
8201   PetscCall(DMClone(dm, &rdm));
8202   PetscCall(DMGetDimension(rdm, &dim));
8203   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8204   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8205   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct)+1 ? PETSC_TRUE : PETSC_FALSE;
8206   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8207   PetscCall(PetscObjectSetName((PetscObject) fe, "rank"));
8208   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject) fe));
8209   PetscCall(PetscFEDestroy(&fe));
8210   PetscCall(DMCreateDS(rdm));
8211   PetscCall(DMCreateGlobalVector(rdm, ranks));
8212   PetscCall(PetscObjectSetName((PetscObject) *ranks, "partition"));
8213   PetscCall(VecGetArray(*ranks, &r));
8214   for (c = cStart; c < cEnd; ++c) {
8215     PetscScalar *lr;
8216 
8217     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8218     if (lr) *lr = rank;
8219   }
8220   PetscCall(VecRestoreArray(*ranks, &r));
8221   PetscCall(DMDestroy(&rdm));
8222   PetscFunctionReturn(0);
8223 }
8224 
8225 /*@
8226   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8227 
8228   Input Parameters:
8229 + dm    - The DMPlex
8230 - label - The DMLabel
8231 
8232   Output Parameter:
8233 . val - The label value field
8234 
8235   Options Database Keys:
8236 . -dm_label_view - Adds the label value field into the DM output from -dm_view using the same viewer
8237 
8238   Level: intermediate
8239 
8240 .seealso: `DMView()`
8241 @*/
8242 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8243 {
8244   DM             rdm;
8245   PetscFE        fe;
8246   PetscScalar   *v;
8247   PetscInt       dim, cStart, cEnd, c;
8248 
8249   PetscFunctionBeginUser;
8250   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8251   PetscValidPointer(label, 2);
8252   PetscValidPointer(val, 3);
8253   PetscCall(DMClone(dm, &rdm));
8254   PetscCall(DMGetDimension(rdm, &dim));
8255   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject) rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8256   PetscCall(PetscObjectSetName((PetscObject) fe, "label_value"));
8257   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject) fe));
8258   PetscCall(PetscFEDestroy(&fe));
8259   PetscCall(DMCreateDS(rdm));
8260   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8261   PetscCall(DMCreateGlobalVector(rdm, val));
8262   PetscCall(PetscObjectSetName((PetscObject) *val, "label_value"));
8263   PetscCall(VecGetArray(*val, &v));
8264   for (c = cStart; c < cEnd; ++c) {
8265     PetscScalar *lv;
8266     PetscInt     cval;
8267 
8268     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8269     PetscCall(DMLabelGetValue(label, c, &cval));
8270     *lv = cval;
8271   }
8272   PetscCall(VecRestoreArray(*val, &v));
8273   PetscCall(DMDestroy(&rdm));
8274   PetscFunctionReturn(0);
8275 }
8276 
8277 /*@
8278   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8279 
8280   Input Parameter:
8281 . dm - The DMPlex object
8282 
8283   Notes:
8284   This is a useful diagnostic when creating meshes programmatically.
8285 
8286   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8287 
8288   Level: developer
8289 
8290 .seealso: `DMCreate()`, `DMSetFromOptions()`
8291 @*/
8292 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8293 {
8294   PetscSection    coneSection, supportSection;
8295   const PetscInt *cone, *support;
8296   PetscInt        coneSize, c, supportSize, s;
8297   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8298   PetscBool       storagecheck = PETSC_TRUE;
8299 
8300   PetscFunctionBegin;
8301   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8302   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8303   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8304   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8305   /* Check that point p is found in the support of its cone points, and vice versa */
8306   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8307   for (p = pStart; p < pEnd; ++p) {
8308     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8309     PetscCall(DMPlexGetCone(dm, p, &cone));
8310     for (c = 0; c < coneSize; ++c) {
8311       PetscBool dup = PETSC_FALSE;
8312       PetscInt  d;
8313       for (d = c-1; d >= 0; --d) {
8314         if (cone[c] == cone[d]) {dup = PETSC_TRUE; break;}
8315       }
8316       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8317       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8318       for (s = 0; s < supportSize; ++s) {
8319         if (support[s] == p) break;
8320       }
8321       if ((s >= supportSize) || (dup && (support[s+1] != p))) {
8322         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8323         for (s = 0; s < coneSize; ++s) {
8324           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8325         }
8326         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8327         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8328         for (s = 0; s < supportSize; ++s) {
8329           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8330         }
8331         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8332         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]);
8333         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8334       }
8335     }
8336     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8337     if (p != pp) { storagecheck = PETSC_FALSE; continue; }
8338     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8339     PetscCall(DMPlexGetSupport(dm, p, &support));
8340     for (s = 0; s < supportSize; ++s) {
8341       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8342       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8343       for (c = 0; c < coneSize; ++c) {
8344         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8345         if (cone[c] != pp) { c = 0; break; }
8346         if (cone[c] == p) break;
8347       }
8348       if (c >= coneSize) {
8349         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8350         for (c = 0; c < supportSize; ++c) {
8351           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8352         }
8353         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8354         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8355         for (c = 0; c < coneSize; ++c) {
8356           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8357         }
8358         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8359         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8360       }
8361     }
8362   }
8363   if (storagecheck) {
8364     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8365     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8366     PetscCheck(csize == ssize,PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8367   }
8368   PetscFunctionReturn(0);
8369 }
8370 
8371 /*
8372   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.
8373 */
8374 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
8375 {
8376   DMPolytopeType  cct;
8377   PetscInt        ptpoints[4];
8378   const PetscInt *cone, *ccone, *ptcone;
8379   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8380 
8381   PetscFunctionBegin;
8382   *unsplit = 0;
8383   switch (ct) {
8384     case DM_POLYTOPE_POINT_PRISM_TENSOR:
8385       ptpoints[npt++] = c;
8386       break;
8387     case DM_POLYTOPE_SEG_PRISM_TENSOR:
8388       PetscCall(DMPlexGetCone(dm, c, &cone));
8389       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8390       for (cp = 0; cp < coneSize; ++cp) {
8391         PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8392         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8393       }
8394       break;
8395     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8396     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8397       PetscCall(DMPlexGetCone(dm, c, &cone));
8398       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8399       for (cp = 0; cp < coneSize; ++cp) {
8400         PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8401         PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8402         for (ccp = 0; ccp < cconeSize; ++ccp) {
8403           PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8404           if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8405             PetscInt p;
8406             for (p = 0; p < npt; ++p) if (ptpoints[p] == ccone[ccp]) break;
8407             if (p == npt) ptpoints[npt++] = ccone[ccp];
8408           }
8409         }
8410       }
8411       break;
8412     default: break;
8413   }
8414   for (pt = 0; pt < npt; ++pt) {
8415     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8416     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8417   }
8418   PetscFunctionReturn(0);
8419 }
8420 
8421 /*@
8422   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8423 
8424   Input Parameters:
8425 + dm - The DMPlex object
8426 - cellHeight - Normally 0
8427 
8428   Notes:
8429   This is a useful diagnostic when creating meshes programmatically.
8430   Currently applicable only to homogeneous simplex or tensor meshes.
8431 
8432   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8433 
8434   Level: developer
8435 
8436 .seealso: `DMCreate()`, `DMSetFromOptions()`
8437 @*/
8438 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
8439 {
8440   DMPlexInterpolatedFlag interp;
8441   DMPolytopeType         ct;
8442   PetscInt               vStart, vEnd, cStart, cEnd, c;
8443 
8444   PetscFunctionBegin;
8445   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8446   PetscCall(DMPlexIsInterpolated(dm, &interp));
8447   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8448   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8449   for (c = cStart; c < cEnd; ++c) {
8450     PetscInt *closure = NULL;
8451     PetscInt  coneSize, closureSize, cl, Nv = 0;
8452 
8453     PetscCall(DMPlexGetCellType(dm, c, &ct));
8454     PetscCheck((PetscInt) ct >= 0,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
8455     if (ct == DM_POLYTOPE_UNKNOWN) continue;
8456     if (interp == DMPLEX_INTERPOLATED_FULL) {
8457       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8458       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));
8459     }
8460     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8461     for (cl = 0; cl < closureSize*2; cl += 2) {
8462       const PetscInt p = closure[cl];
8463       if ((p >= vStart) && (p < vEnd)) ++Nv;
8464     }
8465     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8466     /* Special Case: Tensor faces with identified vertices */
8467     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
8468       PetscInt unsplit;
8469 
8470       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8471       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
8472     }
8473     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));
8474   }
8475   PetscFunctionReturn(0);
8476 }
8477 
8478 /*@
8479   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
8480 
8481   Collective
8482 
8483   Input Parameters:
8484 + dm - The DMPlex object
8485 - cellHeight - Normally 0
8486 
8487   Notes:
8488   This is a useful diagnostic when creating meshes programmatically.
8489   This routine is only relevant for meshes that are fully interpolated across all ranks.
8490   It will error out if a partially interpolated mesh is given on some rank.
8491   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
8492 
8493   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8494 
8495   Level: developer
8496 
8497 .seealso: `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
8498 @*/
8499 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
8500 {
8501   PetscInt       dim, depth, vStart, vEnd, cStart, cEnd, c, h;
8502   DMPlexInterpolatedFlag interpEnum;
8503 
8504   PetscFunctionBegin;
8505   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8506   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
8507   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(0);
8508   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
8509     PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported");
8510     PetscFunctionReturn(0);
8511   }
8512 
8513   PetscCall(DMGetDimension(dm, &dim));
8514   PetscCall(DMPlexGetDepth(dm, &depth));
8515   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8516   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
8517     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
8518     for (c = cStart; c < cEnd; ++c) {
8519       const PetscInt      *cone, *ornt, *faceSizes, *faces;
8520       const DMPolytopeType *faceTypes;
8521       DMPolytopeType        ct;
8522       PetscInt              numFaces, coneSize, f;
8523       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
8524 
8525       PetscCall(DMPlexGetCellType(dm, c, &ct));
8526       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8527       if (unsplit) continue;
8528       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8529       PetscCall(DMPlexGetCone(dm, c, &cone));
8530       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
8531       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8532       for (cl = 0; cl < closureSize*2; cl += 2) {
8533         const PetscInt p = closure[cl];
8534         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
8535       }
8536       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8537       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);
8538       for (f = 0; f < numFaces; ++f) {
8539         DMPolytopeType fct;
8540         PetscInt       *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
8541 
8542         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
8543         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
8544         for (cl = 0; cl < fclosureSize*2; cl += 2) {
8545           const PetscInt p = fclosure[cl];
8546           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
8547         }
8548         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]);
8549         for (v = 0; v < fnumCorners; ++v) {
8550           if (fclosure[v] != faces[fOff+v]) {
8551             PetscInt v1;
8552 
8553             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
8554             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
8555             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
8556             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff+v1]));
8557             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8558             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]);
8559           }
8560         }
8561         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
8562         fOff += faceSizes[f];
8563       }
8564       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8565       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8566     }
8567   }
8568   PetscFunctionReturn(0);
8569 }
8570 
8571 /*@
8572   DMPlexCheckGeometry - Check the geometry of mesh cells
8573 
8574   Input Parameter:
8575 . dm - The DMPlex object
8576 
8577   Notes:
8578   This is a useful diagnostic when creating meshes programmatically.
8579 
8580   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8581 
8582   Level: developer
8583 
8584 .seealso: `DMCreate()`, `DMSetFromOptions()`
8585 @*/
8586 PetscErrorCode DMPlexCheckGeometry(DM dm)
8587 {
8588   Vec            coordinates;
8589   PetscReal      detJ, J[9], refVol = 1.0;
8590   PetscReal      vol;
8591   PetscBool      periodic;
8592   PetscInt       dim, depth, dE, d, cStart, cEnd, c;
8593 
8594   PetscFunctionBegin;
8595   PetscCall(DMGetDimension(dm, &dim));
8596   PetscCall(DMGetCoordinateDim(dm, &dE));
8597   if (dim != dE) PetscFunctionReturn(0);
8598   PetscCall(DMPlexGetDepth(dm, &depth));
8599   PetscCall(DMGetPeriodicity(dm, &periodic, NULL, NULL, NULL));
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     if (depth > 1 && !periodic) {
8631       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
8632       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);
8633       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double) vol));
8634     }
8635   }
8636   PetscFunctionReturn(0);
8637 }
8638 
8639 /*@
8640   DMPlexCheckPointSF - Check that several necessary conditions are met for the Point SF of this plex.
8641 
8642   Collective
8643 
8644   Input Parameters:
8645 + dm - The DMPlex object
8646 - pointSF - The Point SF, or NULL for Point SF attached to DM
8647 
8648   Notes:
8649   This is mainly intended for debugging/testing purposes.
8650 
8651   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8652 
8653   Level: developer
8654 
8655 .seealso: `DMGetPointSF()`, `DMSetFromOptions()`
8656 @*/
8657 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF)
8658 {
8659   PetscInt        l, nleaves, nroots, overlap;
8660   const PetscInt *locals;
8661   const PetscSFNode *remotes;
8662   PetscBool       distributed;
8663   MPI_Comm        comm;
8664   PetscMPIInt     rank;
8665 
8666   PetscFunctionBegin;
8667   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8668   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
8669   else         pointSF = dm->sf;
8670   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
8671   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
8672   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8673   {
8674     PetscMPIInt    mpiFlag;
8675 
8676     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF),&mpiFlag));
8677     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)",mpiFlag);
8678   }
8679   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
8680   PetscCall(DMPlexIsDistributed(dm, &distributed));
8681   if (!distributed) {
8682     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);
8683     PetscFunctionReturn(0);
8684   }
8685   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);
8686   PetscCall(DMPlexGetOverlap(dm, &overlap));
8687 
8688   /* Check SF graph is compatible with DMPlex chart */
8689   {
8690     PetscInt pStart, pEnd, maxLeaf;
8691 
8692     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8693     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
8694     PetscCheck(pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd-pStart, nroots);
8695     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
8696   }
8697 
8698   /* Check Point SF has no local points referenced */
8699   for (l = 0; l < nleaves; l++) {
8700     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);
8701   }
8702 
8703   /* Check there are no cells in interface */
8704   if (!overlap) {
8705     PetscInt cellHeight, cStart, cEnd;
8706 
8707     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8708     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8709     for (l = 0; l < nleaves; ++l) {
8710       const PetscInt point = locals ? locals[l] : l;
8711 
8712       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
8713     }
8714   }
8715 
8716   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
8717   {
8718     const PetscInt *rootdegree;
8719 
8720     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
8721     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
8722     for (l = 0; l < nleaves; ++l) {
8723       const PetscInt  point = locals ? locals[l] : l;
8724       const PetscInt *cone;
8725       PetscInt        coneSize, c, idx;
8726 
8727       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
8728       PetscCall(DMPlexGetCone(dm, point, &cone));
8729       for (c = 0; c < coneSize; ++c) {
8730         if (!rootdegree[cone[c]]) {
8731           if (locals) {
8732             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
8733           } else {
8734             idx = (cone[c] < nleaves) ? cone[c] : -1;
8735           }
8736           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
8737         }
8738       }
8739     }
8740   }
8741   PetscFunctionReturn(0);
8742 }
8743 
8744 /*@
8745   DMPlexCheck - Perform various checks of Plex sanity
8746 
8747   Input Parameter:
8748 . dm - The DMPlex object
8749 
8750   Notes:
8751   This is a useful diagnostic when creating meshes programmatically.
8752 
8753   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8754 
8755   Currently does not include DMPlexCheckCellShape().
8756 
8757   Level: developer
8758 
8759 .seealso: DMCreate(), DMSetFromOptions()
8760 @*/
8761 PetscErrorCode DMPlexCheck(DM dm)
8762 {
8763   PetscInt cellHeight;
8764 
8765   PetscFunctionBegin;
8766   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8767   PetscCall(DMPlexCheckSymmetry(dm));
8768   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
8769   PetscCall(DMPlexCheckFaces(dm, cellHeight));
8770   PetscCall(DMPlexCheckGeometry(dm));
8771   PetscCall(DMPlexCheckPointSF(dm, NULL));
8772   PetscCall(DMPlexCheckInterfaceCones(dm));
8773   PetscFunctionReturn(0);
8774 }
8775 
8776 typedef struct cell_stats
8777 {
8778   PetscReal min, max, sum, squaresum;
8779   PetscInt  count;
8780 } cell_stats_t;
8781 
8782 static void MPIAPI cell_stats_reduce(void *a, void *b, int * len, MPI_Datatype *datatype)
8783 {
8784   PetscInt i, N = *len;
8785 
8786   for (i = 0; i < N; i++) {
8787     cell_stats_t *A = (cell_stats_t *) a;
8788     cell_stats_t *B = (cell_stats_t *) b;
8789 
8790     B->min = PetscMin(A->min,B->min);
8791     B->max = PetscMax(A->max,B->max);
8792     B->sum += A->sum;
8793     B->squaresum += A->squaresum;
8794     B->count += A->count;
8795   }
8796 }
8797 
8798 /*@
8799   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
8800 
8801   Collective on dm
8802 
8803   Input Parameters:
8804 + dm        - The DMPlex object
8805 . output    - If true, statistics will be displayed on stdout
8806 - condLimit - Display all cells above this condition number, or PETSC_DETERMINE for no cell output
8807 
8808   Notes:
8809   This is mainly intended for debugging/testing purposes.
8810 
8811   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8812 
8813   Level: developer
8814 
8815 .seealso: `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
8816 @*/
8817 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
8818 {
8819   DM             dmCoarse;
8820   cell_stats_t   stats, globalStats;
8821   MPI_Comm       comm = PetscObjectComm((PetscObject)dm);
8822   PetscReal      *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
8823   PetscReal      limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
8824   PetscInt       cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
8825   PetscMPIInt    rank,size;
8826 
8827   PetscFunctionBegin;
8828   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8829   stats.min   = PETSC_MAX_REAL;
8830   stats.max   = PETSC_MIN_REAL;
8831   stats.sum   = stats.squaresum = 0.;
8832   stats.count = 0;
8833 
8834   PetscCallMPI(MPI_Comm_size(comm, &size));
8835   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8836   PetscCall(DMGetCoordinateDim(dm,&cdim));
8837   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
8838   PetscCall(DMPlexGetSimplexOrBoxCells(dm,0,&cStart,&cEnd));
8839   PetscCall(DMPlexGetDepthStratum(dm,1,&eStart,&eEnd));
8840   for (c = cStart; c < cEnd; c++) {
8841     PetscInt  i;
8842     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
8843 
8844     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm,c,NULL,J,invJ,&detJ));
8845     PetscCheck(detJ >= 0.0,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
8846     for (i = 0; i < PetscSqr(cdim); ++i) {
8847       frobJ    += J[i] * J[i];
8848       frobInvJ += invJ[i] * invJ[i];
8849     }
8850     cond2 = frobJ * frobInvJ;
8851     cond  = PetscSqrtReal(cond2);
8852 
8853     stats.min        = PetscMin(stats.min,cond);
8854     stats.max        = PetscMax(stats.max,cond);
8855     stats.sum       += cond;
8856     stats.squaresum += cond2;
8857     stats.count++;
8858     if (output && cond > limit) {
8859       PetscSection coordSection;
8860       Vec          coordsLocal;
8861       PetscScalar *coords = NULL;
8862       PetscInt     Nv, d, clSize, cl, *closure = NULL;
8863 
8864       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
8865       PetscCall(DMGetCoordinateSection(dm, &coordSection));
8866       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
8867       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double) cond));
8868       for (i = 0; i < Nv/cdim; ++i) {
8869         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
8870         for (d = 0; d < cdim; ++d) {
8871           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
8872           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double) PetscRealPart(coords[i*cdim+d])));
8873         }
8874         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
8875       }
8876       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
8877       for (cl = 0; cl < clSize*2; cl += 2) {
8878         const PetscInt edge = closure[cl];
8879 
8880         if ((edge >= eStart) && (edge < eEnd)) {
8881           PetscReal len;
8882 
8883           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
8884           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double) len));
8885         }
8886       }
8887       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
8888       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
8889     }
8890   }
8891   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
8892 
8893   if (size > 1) {
8894     PetscMPIInt   blockLengths[2] = {4,1};
8895     MPI_Aint      blockOffsets[2] = {offsetof(cell_stats_t,min),offsetof(cell_stats_t,count)};
8896     MPI_Datatype  blockTypes[2]   = {MPIU_REAL,MPIU_INT}, statType;
8897     MPI_Op        statReduce;
8898 
8899     PetscCallMPI(MPI_Type_create_struct(2,blockLengths,blockOffsets,blockTypes,&statType));
8900     PetscCallMPI(MPI_Type_commit(&statType));
8901     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
8902     PetscCallMPI(MPI_Reduce(&stats,&globalStats,1,statType,statReduce,0,comm));
8903     PetscCallMPI(MPI_Op_free(&statReduce));
8904     PetscCallMPI(MPI_Type_free(&statType));
8905   } else {
8906     PetscCall(PetscArraycpy(&globalStats,&stats,1));
8907   }
8908   if (rank == 0) {
8909     count = globalStats.count;
8910     min   = globalStats.min;
8911     max   = globalStats.max;
8912     mean  = globalStats.sum / globalStats.count;
8913     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1),0)) : 0.0;
8914   }
8915 
8916   if (output) {
8917     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));
8918   }
8919   PetscCall(PetscFree2(J,invJ));
8920 
8921   PetscCall(DMGetCoarseDM(dm,&dmCoarse));
8922   if (dmCoarse) {
8923     PetscBool isplex;
8924 
8925     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse,DMPLEX,&isplex));
8926     if (isplex) {
8927       PetscCall(DMPlexCheckCellShape(dmCoarse,output,condLimit));
8928     }
8929   }
8930   PetscFunctionReturn(0);
8931 }
8932 
8933 /*@
8934   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
8935   orthogonal quality below given tolerance.
8936 
8937   Collective on dm
8938 
8939   Input Parameters:
8940 + dm   - The DMPlex object
8941 . fv   - Optional PetscFV object for pre-computed cell/face centroid information
8942 - atol - [0, 1] Absolute tolerance for tagging cells.
8943 
8944   Output Parameters:
8945 + OrthQual      - Vec containing orthogonal quality per cell
8946 - OrthQualLabel - DMLabel tagging cells below atol with DM_ADAPT_REFINE
8947 
8948   Options Database Keys:
8949 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only PETSCVIEWERASCII is
8950 supported.
8951 - -dm_plex_orthogonal_quality_vec_view - view OrthQual vector.
8952 
8953   Notes:
8954   Orthogonal quality is given by the following formula:
8955 
8956   \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]
8957 
8958   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
8959   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
8960   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
8961   calculating the cosine of the angle between these vectors.
8962 
8963   Orthogonal quality ranges from 1 (best) to 0 (worst).
8964 
8965   This routine is mainly useful for FVM, however is not restricted to only FVM. The PetscFV object is optionally used to check for
8966   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
8967 
8968   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
8969 
8970   Level: intermediate
8971 
8972 .seealso: `DMPlexCheckCellShape()`, `DMCreateLabel()`
8973 @*/
8974 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
8975 {
8976   PetscInt                nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
8977   PetscInt                *idx;
8978   PetscScalar             *oqVals;
8979   const PetscScalar       *cellGeomArr, *faceGeomArr;
8980   PetscReal               *ci, *fi, *Ai;
8981   MPI_Comm                comm;
8982   Vec                     cellgeom, facegeom;
8983   DM                      dmFace, dmCell;
8984   IS                      glob;
8985   ISLocalToGlobalMapping  ltog;
8986   PetscViewer             vwr;
8987 
8988   PetscFunctionBegin;
8989   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8990   if (fv) {PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);}
8991   PetscValidPointer(OrthQual, 4);
8992   PetscCheck(atol >= 0.0 && atol <= 1.0,PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Absolute tolerance %g not in [0,1]",(double)atol);
8993   PetscCall(PetscObjectGetComm((PetscObject) dm, &comm));
8994   PetscCall(DMGetDimension(dm, &nc));
8995   PetscCheck(nc >= 2,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
8996   {
8997     DMPlexInterpolatedFlag interpFlag;
8998 
8999     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9000     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9001       PetscMPIInt rank;
9002 
9003       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9004       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9005     }
9006   }
9007   if (OrthQualLabel) {
9008     PetscValidPointer(OrthQualLabel, 5);
9009     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9010     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9011   } else {*OrthQualLabel = NULL;}
9012   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9013   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9014   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9015   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9016   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9017   PetscCall(VecCreate(comm, OrthQual));
9018   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9019   PetscCall(VecSetSizes(*OrthQual, cEnd-cStart, PETSC_DETERMINE));
9020   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9021   PetscCall(VecSetUp(*OrthQual));
9022   PetscCall(ISDestroy(&glob));
9023   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9024   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9025   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9026   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9027   PetscCall(VecGetDM(cellgeom, &dmCell));
9028   PetscCall(VecGetDM(facegeom, &dmFace));
9029   PetscCall(PetscMalloc5(cEnd-cStart, &idx, cEnd-cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9030   for (cell = cStart; cell < cEnd; cellIter++,cell++) {
9031     PetscInt           cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9032     PetscInt           cellarr[2], *adj = NULL;
9033     PetscScalar        *cArr, *fArr;
9034     PetscReal          minvalc = 1.0, minvalf = 1.0;
9035     PetscFVCellGeom    *cg;
9036 
9037     idx[cellIter] = cell-cStart;
9038     cellarr[0] = cell;
9039     /* Make indexing into cellGeom easier */
9040     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9041     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9042     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9043     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9044     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++,cellneigh++) {
9045       PetscInt         i;
9046       const PetscInt   neigh = adj[cellneigh];
9047       PetscReal        normci = 0, normfi = 0, normai = 0;
9048       PetscFVCellGeom  *cgneigh;
9049       PetscFVFaceGeom  *fg;
9050 
9051       /* Don't count ourselves in the neighbor list */
9052       if (neigh == cell) continue;
9053       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9054       cellarr[1] = neigh;
9055       {
9056         PetscInt       numcovpts;
9057         const PetscInt *covpts;
9058 
9059         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9060         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9061         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9062       }
9063 
9064       /* Compute c_i, f_i and their norms */
9065       for (i = 0; i < nc; i++) {
9066         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9067         fi[i] = fg->centroid[i] - cg->centroid[i];
9068         Ai[i] = fg->normal[i];
9069         normci += PetscPowReal(ci[i], 2);
9070         normfi += PetscPowReal(fi[i], 2);
9071         normai += PetscPowReal(Ai[i], 2);
9072       }
9073       normci = PetscSqrtReal(normci);
9074       normfi = PetscSqrtReal(normfi);
9075       normai = PetscSqrtReal(normai);
9076 
9077       /* Normalize and compute for each face-cell-normal pair */
9078       for (i = 0; i < nc; i++) {
9079         ci[i] = ci[i]/normci;
9080         fi[i] = fi[i]/normfi;
9081         Ai[i] = Ai[i]/normai;
9082         /* PetscAbs because I don't know if normals are guaranteed to point out */
9083         cArr[cellneighiter] += PetscAbs(Ai[i]*ci[i]);
9084         fArr[cellneighiter] += PetscAbs(Ai[i]*fi[i]);
9085       }
9086       if (PetscRealPart(cArr[cellneighiter]) < minvalc) {
9087         minvalc = PetscRealPart(cArr[cellneighiter]);
9088       }
9089       if (PetscRealPart(fArr[cellneighiter]) < minvalf) {
9090         minvalf = PetscRealPart(fArr[cellneighiter]);
9091       }
9092     }
9093     PetscCall(PetscFree(adj));
9094     PetscCall(PetscFree2(cArr, fArr));
9095     /* Defer to cell if they're equal */
9096     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9097     if (OrthQualLabel) {
9098       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9099     }
9100   }
9101   PetscCall(VecSetValuesLocal(*OrthQual, cEnd-cStart, idx, oqVals, INSERT_VALUES));
9102   PetscCall(VecAssemblyBegin(*OrthQual));
9103   PetscCall(VecAssemblyEnd(*OrthQual));
9104   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9105   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9106   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9107   if (OrthQualLabel) {
9108     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9109   }
9110   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9111   PetscCall(PetscViewerDestroy(&vwr));
9112   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9113   PetscFunctionReturn(0);
9114 }
9115 
9116 /* this is here insead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9117  * interpolator construction */
9118 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9119 {
9120   PetscSection   section, newSection, gsection;
9121   PetscSF        sf;
9122   PetscBool      hasConstraints, ghasConstraints;
9123 
9124   PetscFunctionBegin;
9125   PetscValidHeaderSpecific(dm,DM_CLASSID,1);
9126   PetscValidPointer(odm,2);
9127   PetscCall(DMGetLocalSection(dm, &section));
9128   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9129   PetscCallMPI(MPI_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject) dm)));
9130   if (!ghasConstraints) {
9131     PetscCall(PetscObjectReference((PetscObject)dm));
9132     *odm = dm;
9133     PetscFunctionReturn(0);
9134   }
9135   PetscCall(DMClone(dm, odm));
9136   PetscCall(DMCopyFields(dm, *odm));
9137   PetscCall(DMGetLocalSection(*odm, &newSection));
9138   PetscCall(DMGetPointSF(*odm, &sf));
9139   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9140   PetscCall(DMSetGlobalSection(*odm, gsection));
9141   PetscCall(PetscSectionDestroy(&gsection));
9142   PetscFunctionReturn(0);
9143 }
9144 
9145 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9146 {
9147   DM             dmco, dmfo;
9148   Mat            interpo;
9149   Vec            rscale;
9150   Vec            cglobalo, clocal;
9151   Vec            fglobal, fglobalo, flocal;
9152   PetscBool      regular;
9153 
9154   PetscFunctionBegin;
9155   PetscCall(DMGetFullDM(dmc, &dmco));
9156   PetscCall(DMGetFullDM(dmf, &dmfo));
9157   PetscCall(DMSetCoarseDM(dmfo, dmco));
9158   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9159   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9160   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9161   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9162   PetscCall(DMCreateLocalVector(dmc, &clocal));
9163   PetscCall(VecSet(cglobalo, 0.));
9164   PetscCall(VecSet(clocal, 0.));
9165   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9166   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9167   PetscCall(DMCreateLocalVector(dmf, &flocal));
9168   PetscCall(VecSet(fglobal, 0.));
9169   PetscCall(VecSet(fglobalo, 0.));
9170   PetscCall(VecSet(flocal, 0.));
9171   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9172   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9173   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9174   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9175   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9176   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9177   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9178   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9179   *shift = fglobal;
9180   PetscCall(VecDestroy(&flocal));
9181   PetscCall(VecDestroy(&fglobalo));
9182   PetscCall(VecDestroy(&clocal));
9183   PetscCall(VecDestroy(&cglobalo));
9184   PetscCall(VecDestroy(&rscale));
9185   PetscCall(MatDestroy(&interpo));
9186   PetscCall(DMDestroy(&dmfo));
9187   PetscCall(DMDestroy(&dmco));
9188   PetscFunctionReturn(0);
9189 }
9190 
9191 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9192 {
9193   PetscObject    shifto;
9194   Vec            shift;
9195 
9196   PetscFunctionBegin;
9197   if (!interp) {
9198     Vec rscale;
9199 
9200     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9201     PetscCall(VecDestroy(&rscale));
9202   } else {
9203     PetscCall(PetscObjectReference((PetscObject)interp));
9204   }
9205   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9206   if (!shifto) {
9207     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9208     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject) shift));
9209     shifto = (PetscObject) shift;
9210     PetscCall(VecDestroy(&shift));
9211   }
9212   shift = (Vec) shifto;
9213   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9214   PetscCall(VecAXPY(fineSol, 1.0, shift));
9215   PetscCall(MatDestroy(&interp));
9216   PetscFunctionReturn(0);
9217 }
9218 
9219 /* Pointwise interpolation
9220      Just code FEM for now
9221      u^f = I u^c
9222      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9223      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9224      I_{ij} = psi^f_i phi^c_j
9225 */
9226 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9227 {
9228   PetscSection   gsc, gsf;
9229   PetscInt       m, n;
9230   void          *ctx;
9231   DM             cdm;
9232   PetscBool      regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9233 
9234   PetscFunctionBegin;
9235   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9236   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9237   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9238   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9239 
9240   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9241   PetscCall(MatCreate(PetscObjectComm((PetscObject) dmCoarse), interpolation));
9242   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9243   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9244   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9245 
9246   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9247   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9248   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9249   else                                            PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9250   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9251   if (scaling) {
9252     /* Use naive scaling */
9253     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9254   }
9255   PetscFunctionReturn(0);
9256 }
9257 
9258 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9259 {
9260   VecScatter     ctx;
9261 
9262   PetscFunctionBegin;
9263   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9264   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9265   PetscCall(VecScatterDestroy(&ctx));
9266   PetscFunctionReturn(0);
9267 }
9268 
9269 static void g0_identity_private(PetscInt dim, PetscInt Nf, PetscInt NfAux,
9270                                 const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[],
9271                                 const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[],
9272                                 PetscReal t, PetscReal u_tShift, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar g0[])
9273 {
9274   const PetscInt Nc = uOff[1] - uOff[0];
9275   PetscInt       c;
9276   for (c = 0; c < Nc; ++c) g0[c*Nc+c] = 1.0;
9277 }
9278 
9279 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9280 {
9281   DM             dmc;
9282   PetscDS        ds;
9283   Vec            ones, locmass;
9284   IS             cellIS;
9285   PetscFormKey   key;
9286   PetscInt       depth;
9287 
9288   PetscFunctionBegin;
9289   PetscCall(DMClone(dm, &dmc));
9290   PetscCall(DMCopyDisc(dm, dmc));
9291   PetscCall(DMGetDS(dmc, &ds));
9292   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9293   PetscCall(DMCreateGlobalVector(dmc, mass));
9294   PetscCall(DMGetLocalVector(dmc, &ones));
9295   PetscCall(DMGetLocalVector(dmc, &locmass));
9296   PetscCall(DMPlexGetDepth(dmc, &depth));
9297   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9298   PetscCall(VecSet(locmass, 0.0));
9299   PetscCall(VecSet(ones, 1.0));
9300   key.label = NULL;
9301   key.value = 0;
9302   key.field = 0;
9303   key.part  = 0;
9304   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9305   PetscCall(ISDestroy(&cellIS));
9306   PetscCall(VecSet(*mass, 0.0));
9307   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9308   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9309   PetscCall(DMRestoreLocalVector(dmc, &ones));
9310   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9311   PetscCall(DMDestroy(&dmc));
9312   PetscFunctionReturn(0);
9313 }
9314 
9315 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9316 {
9317   PetscSection   gsc, gsf;
9318   PetscInt       m, n;
9319   void          *ctx;
9320   DM             cdm;
9321   PetscBool      regular;
9322 
9323   PetscFunctionBegin;
9324   if (dmFine == dmCoarse) {
9325     DM            dmc;
9326     PetscDS       ds;
9327     PetscWeakForm wf;
9328     Vec           u;
9329     IS            cellIS;
9330     PetscFormKey  key;
9331     PetscInt      depth;
9332 
9333     PetscCall(DMClone(dmFine, &dmc));
9334     PetscCall(DMCopyDisc(dmFine, dmc));
9335     PetscCall(DMGetDS(dmc, &ds));
9336     PetscCall(PetscDSGetWeakForm(ds, &wf));
9337     PetscCall(PetscWeakFormClear(wf));
9338     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9339     PetscCall(DMCreateMatrix(dmc, mass));
9340     PetscCall(DMGetGlobalVector(dmc, &u));
9341     PetscCall(DMPlexGetDepth(dmc, &depth));
9342     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9343     PetscCall(MatZeroEntries(*mass));
9344     key.label = NULL;
9345     key.value = 0;
9346     key.field = 0;
9347     key.part  = 0;
9348     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9349     PetscCall(ISDestroy(&cellIS));
9350     PetscCall(DMRestoreGlobalVector(dmc, &u));
9351     PetscCall(DMDestroy(&dmc));
9352   } else {
9353     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9354     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9355     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9356     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9357 
9358     PetscCall(MatCreate(PetscObjectComm((PetscObject) dmCoarse), mass));
9359     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9360     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9361     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9362 
9363     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9364     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9365     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9366     else                            PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9367   }
9368   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9369   PetscFunctionReturn(0);
9370 }
9371 
9372 /*@
9373   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9374 
9375   Input Parameter:
9376 . dm - The DMPlex object
9377 
9378   Output Parameter:
9379 . regular - The flag
9380 
9381   Level: intermediate
9382 
9383 .seealso: `DMPlexSetRegularRefinement()`
9384 @*/
9385 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
9386 {
9387   PetscFunctionBegin;
9388   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9389   PetscValidBoolPointer(regular, 2);
9390   *regular = ((DM_Plex *) dm->data)->regularRefinement;
9391   PetscFunctionReturn(0);
9392 }
9393 
9394 /*@
9395   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9396 
9397   Input Parameters:
9398 + dm - The DMPlex object
9399 - regular - The flag
9400 
9401   Level: intermediate
9402 
9403 .seealso: `DMPlexGetRegularRefinement()`
9404 @*/
9405 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
9406 {
9407   PetscFunctionBegin;
9408   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9409   ((DM_Plex *) dm->data)->regularRefinement = regular;
9410   PetscFunctionReturn(0);
9411 }
9412 
9413 /* anchors */
9414 /*@
9415   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9416   call DMPlexGetAnchors() directly: if there are anchors, then DMPlexGetAnchors() is called during DMGetDefaultConstraints().
9417 
9418   not collective
9419 
9420   Input Parameter:
9421 . dm - The DMPlex object
9422 
9423   Output Parameters:
9424 + anchorSection - If not NULL, set to the section describing which points anchor the constrained points.
9425 - anchorIS - If not NULL, set to the list of anchors indexed by anchorSection
9426 
9427   Level: intermediate
9428 
9429 .seealso: `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9430 @*/
9431 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
9432 {
9433   DM_Plex *plex = (DM_Plex *)dm->data;
9434 
9435   PetscFunctionBegin;
9436   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9437   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9438   if (anchorSection) *anchorSection = plex->anchorSection;
9439   if (anchorIS) *anchorIS = plex->anchorIS;
9440   PetscFunctionReturn(0);
9441 }
9442 
9443 /*@
9444   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.  Unlike boundary conditions,
9445   when a point's degrees of freedom in a section are constrained to an outside value, the anchor constraints set a
9446   point's degrees of freedom to be a linear combination of other points' degrees of freedom.
9447 
9448   After specifying the layout of constraints with DMPlexSetAnchors(), one specifies the constraints by calling
9449   DMGetDefaultConstraints() and filling in the entries in the constraint matrix.
9450 
9451   collective on dm
9452 
9453   Input Parameters:
9454 + dm - The DMPlex object
9455 . 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).
9456 - anchorIS - The list of all anchor points.  Must have a local communicator (PETSC_COMM_SELF or derivative).
9457 
9458   The reference counts of anchorSection and anchorIS are incremented.
9459 
9460   Level: intermediate
9461 
9462 .seealso: `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9463 @*/
9464 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
9465 {
9466   DM_Plex        *plex = (DM_Plex *)dm->data;
9467   PetscMPIInt    result;
9468 
9469   PetscFunctionBegin;
9470   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9471   if (anchorSection) {
9472     PetscValidHeaderSpecific(anchorSection,PETSC_SECTION_CLASSID,2);
9473     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF,PetscObjectComm((PetscObject)anchorSection),&result));
9474     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT,PETSC_COMM_SELF,PETSC_ERR_ARG_NOTSAMECOMM,"anchor section must have local communicator");
9475   }
9476   if (anchorIS) {
9477     PetscValidHeaderSpecific(anchorIS,IS_CLASSID,3);
9478     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF,PetscObjectComm((PetscObject)anchorIS),&result));
9479     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT,PETSC_COMM_SELF,PETSC_ERR_ARG_NOTSAMECOMM,"anchor IS must have local communicator");
9480   }
9481 
9482   PetscCall(PetscObjectReference((PetscObject)anchorSection));
9483   PetscCall(PetscSectionDestroy(&plex->anchorSection));
9484   plex->anchorSection = anchorSection;
9485 
9486   PetscCall(PetscObjectReference((PetscObject)anchorIS));
9487   PetscCall(ISDestroy(&plex->anchorIS));
9488   plex->anchorIS = anchorIS;
9489 
9490   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
9491     PetscInt size, a, pStart, pEnd;
9492     const PetscInt *anchors;
9493 
9494     PetscCall(PetscSectionGetChart(anchorSection,&pStart,&pEnd));
9495     PetscCall(ISGetLocalSize(anchorIS,&size));
9496     PetscCall(ISGetIndices(anchorIS,&anchors));
9497     for (a = 0; a < size; a++) {
9498       PetscInt p;
9499 
9500       p = anchors[a];
9501       if (p >= pStart && p < pEnd) {
9502         PetscInt dof;
9503 
9504         PetscCall(PetscSectionGetDof(anchorSection,p,&dof));
9505         if (dof) {
9506 
9507           PetscCall(ISRestoreIndices(anchorIS,&anchors));
9508           SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Point %" PetscInt_FMT " cannot be constrained and an anchor",p);
9509         }
9510       }
9511     }
9512     PetscCall(ISRestoreIndices(anchorIS,&anchors));
9513   }
9514   /* reset the generic constraints */
9515   PetscCall(DMSetDefaultConstraints(dm,NULL,NULL,NULL));
9516   PetscFunctionReturn(0);
9517 }
9518 
9519 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
9520 {
9521   PetscSection anchorSection;
9522   PetscInt pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
9523 
9524   PetscFunctionBegin;
9525   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9526   PetscCall(DMPlexGetAnchors(dm,&anchorSection,NULL));
9527   PetscCall(PetscSectionCreate(PETSC_COMM_SELF,cSec));
9528   PetscCall(PetscSectionGetNumFields(section,&numFields));
9529   if (numFields) {
9530     PetscInt f;
9531     PetscCall(PetscSectionSetNumFields(*cSec,numFields));
9532 
9533     for (f = 0; f < numFields; f++) {
9534       PetscInt numComp;
9535 
9536       PetscCall(PetscSectionGetFieldComponents(section,f,&numComp));
9537       PetscCall(PetscSectionSetFieldComponents(*cSec,f,numComp));
9538     }
9539   }
9540   PetscCall(PetscSectionGetChart(anchorSection,&pStart,&pEnd));
9541   PetscCall(PetscSectionGetChart(section,&sStart,&sEnd));
9542   pStart = PetscMax(pStart,sStart);
9543   pEnd   = PetscMin(pEnd,sEnd);
9544   pEnd   = PetscMax(pStart,pEnd);
9545   PetscCall(PetscSectionSetChart(*cSec,pStart,pEnd));
9546   for (p = pStart; p < pEnd; p++) {
9547     PetscCall(PetscSectionGetDof(anchorSection,p,&dof));
9548     if (dof) {
9549       PetscCall(PetscSectionGetDof(section,p,&dof));
9550       PetscCall(PetscSectionSetDof(*cSec,p,dof));
9551       for (f = 0; f < numFields; f++) {
9552         PetscCall(PetscSectionGetFieldDof(section,p,f,&dof));
9553         PetscCall(PetscSectionSetFieldDof(*cSec,p,f,dof));
9554       }
9555     }
9556   }
9557   PetscCall(PetscSectionSetUp(*cSec));
9558   PetscCall(PetscObjectSetName((PetscObject) *cSec, "Constraint Section"));
9559   PetscFunctionReturn(0);
9560 }
9561 
9562 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
9563 {
9564   PetscSection   aSec;
9565   PetscInt       pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
9566   const PetscInt *anchors;
9567   PetscInt       numFields, f;
9568   IS             aIS;
9569   MatType        mtype;
9570   PetscBool      iscuda,iskokkos;
9571 
9572   PetscFunctionBegin;
9573   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9574   PetscCall(PetscSectionGetStorageSize(cSec, &m));
9575   PetscCall(PetscSectionGetStorageSize(section, &n));
9576   PetscCall(MatCreate(PETSC_COMM_SELF,cMat));
9577   PetscCall(MatSetSizes(*cMat,m,n,m,n));
9578   PetscCall(PetscStrcmp(dm->mattype,MATSEQAIJCUSPARSE,&iscuda));
9579   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype,MATMPIAIJCUSPARSE,&iscuda));
9580   PetscCall(PetscStrcmp(dm->mattype,MATSEQAIJKOKKOS,&iskokkos));
9581   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype,MATMPIAIJKOKKOS,&iskokkos));
9582   if (iscuda) mtype = MATSEQAIJCUSPARSE;
9583   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
9584   else mtype = MATSEQAIJ;
9585   PetscCall(MatSetType(*cMat,mtype));
9586   PetscCall(DMPlexGetAnchors(dm,&aSec,&aIS));
9587   PetscCall(ISGetIndices(aIS,&anchors));
9588   /* cSec will be a subset of aSec and section */
9589   PetscCall(PetscSectionGetChart(cSec,&pStart,&pEnd));
9590   PetscCall(PetscSectionGetChart(section,&sStart,&sEnd));
9591   PetscCall(PetscMalloc1(m+1,&i));
9592   i[0] = 0;
9593   PetscCall(PetscSectionGetNumFields(section,&numFields));
9594   for (p = pStart; p < pEnd; p++) {
9595     PetscInt rDof, rOff, r;
9596 
9597     PetscCall(PetscSectionGetDof(aSec,p,&rDof));
9598     if (!rDof) continue;
9599     PetscCall(PetscSectionGetOffset(aSec,p,&rOff));
9600     if (numFields) {
9601       for (f = 0; f < numFields; f++) {
9602         annz = 0;
9603         for (r = 0; r < rDof; r++) {
9604           a = anchors[rOff + r];
9605           if (a < sStart || a >= sEnd) continue;
9606           PetscCall(PetscSectionGetFieldDof(section,a,f,&aDof));
9607           annz += aDof;
9608         }
9609         PetscCall(PetscSectionGetFieldDof(cSec,p,f,&dof));
9610         PetscCall(PetscSectionGetFieldOffset(cSec,p,f,&off));
9611         for (q = 0; q < dof; q++) {
9612           i[off + q + 1] = i[off + q] + annz;
9613         }
9614       }
9615     } else {
9616       annz = 0;
9617       PetscCall(PetscSectionGetDof(cSec,p,&dof));
9618       for (q = 0; q < dof; q++) {
9619         a = anchors[rOff + q];
9620         if (a < sStart || a >= sEnd) continue;
9621         PetscCall(PetscSectionGetDof(section,a,&aDof));
9622         annz += aDof;
9623       }
9624       PetscCall(PetscSectionGetDof(cSec,p,&dof));
9625       PetscCall(PetscSectionGetOffset(cSec,p,&off));
9626       for (q = 0; q < dof; q++) {
9627         i[off + q + 1] = i[off + q] + annz;
9628       }
9629     }
9630   }
9631   nnz = i[m];
9632   PetscCall(PetscMalloc1(nnz,&j));
9633   offset = 0;
9634   for (p = pStart; p < pEnd; p++) {
9635     if (numFields) {
9636       for (f = 0; f < numFields; f++) {
9637         PetscCall(PetscSectionGetFieldDof(cSec,p,f,&dof));
9638         for (q = 0; q < dof; q++) {
9639           PetscInt rDof, rOff, r;
9640           PetscCall(PetscSectionGetDof(aSec,p,&rDof));
9641           PetscCall(PetscSectionGetOffset(aSec,p,&rOff));
9642           for (r = 0; r < rDof; r++) {
9643             PetscInt s;
9644 
9645             a = anchors[rOff + r];
9646             if (a < sStart || a >= sEnd) continue;
9647             PetscCall(PetscSectionGetFieldDof(section,a,f,&aDof));
9648             PetscCall(PetscSectionGetFieldOffset(section,a,f,&aOff));
9649             for (s = 0; s < aDof; s++) {
9650               j[offset++] = aOff + s;
9651             }
9652           }
9653         }
9654       }
9655     } else {
9656       PetscCall(PetscSectionGetDof(cSec,p,&dof));
9657       for (q = 0; q < dof; q++) {
9658         PetscInt rDof, rOff, r;
9659         PetscCall(PetscSectionGetDof(aSec,p,&rDof));
9660         PetscCall(PetscSectionGetOffset(aSec,p,&rOff));
9661         for (r = 0; r < rDof; r++) {
9662           PetscInt s;
9663 
9664           a = anchors[rOff + r];
9665           if (a < sStart || a >= sEnd) continue;
9666           PetscCall(PetscSectionGetDof(section,a,&aDof));
9667           PetscCall(PetscSectionGetOffset(section,a,&aOff));
9668           for (s = 0; s < aDof; s++) {
9669             j[offset++] = aOff + s;
9670           }
9671         }
9672       }
9673     }
9674   }
9675   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat,i,j,NULL));
9676   PetscCall(PetscFree(i));
9677   PetscCall(PetscFree(j));
9678   PetscCall(ISRestoreIndices(aIS,&anchors));
9679   PetscFunctionReturn(0);
9680 }
9681 
9682 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
9683 {
9684   DM_Plex        *plex = (DM_Plex *)dm->data;
9685   PetscSection   anchorSection, section, cSec;
9686   Mat            cMat;
9687 
9688   PetscFunctionBegin;
9689   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9690   PetscCall(DMPlexGetAnchors(dm,&anchorSection,NULL));
9691   if (anchorSection) {
9692     PetscInt Nf;
9693 
9694     PetscCall(DMGetLocalSection(dm,&section));
9695     PetscCall(DMPlexCreateConstraintSection_Anchors(dm,section,&cSec));
9696     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm,section,cSec,&cMat));
9697     PetscCall(DMGetNumFields(dm,&Nf));
9698     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm,section,cSec,cMat));
9699     PetscCall(DMSetDefaultConstraints(dm,cSec,cMat,NULL));
9700     PetscCall(PetscSectionDestroy(&cSec));
9701     PetscCall(MatDestroy(&cMat));
9702   }
9703   PetscFunctionReturn(0);
9704 }
9705 
9706 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
9707 {
9708   IS             subis;
9709   PetscSection   section, subsection;
9710 
9711   PetscFunctionBegin;
9712   PetscCall(DMGetLocalSection(dm, &section));
9713   PetscCheck(section,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
9714   PetscCheck(subdm,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
9715   /* Create subdomain */
9716   PetscCall(DMPlexFilter(dm, label, value, subdm));
9717   /* Create submodel */
9718   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
9719   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
9720   PetscCall(DMSetLocalSection(*subdm, subsection));
9721   PetscCall(PetscSectionDestroy(&subsection));
9722   PetscCall(DMCopyDisc(dm, *subdm));
9723   /* Create map from submodel to global model */
9724   if (is) {
9725     PetscSection    sectionGlobal, subsectionGlobal;
9726     IS              spIS;
9727     const PetscInt *spmap;
9728     PetscInt       *subIndices;
9729     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
9730     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
9731 
9732     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
9733     PetscCall(ISGetIndices(spIS, &spmap));
9734     PetscCall(PetscSectionGetNumFields(section, &Nf));
9735     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
9736     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
9737     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
9738     for (p = pStart; p < pEnd; ++p) {
9739       PetscInt gdof, pSubSize  = 0;
9740 
9741       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
9742       if (gdof > 0) {
9743         for (f = 0; f < Nf; ++f) {
9744           PetscInt fdof, fcdof;
9745 
9746           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
9747           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
9748           pSubSize += fdof-fcdof;
9749         }
9750         subSize += pSubSize;
9751         if (pSubSize) {
9752           if (bs < 0) {
9753             bs = pSubSize;
9754           } else if (bs != pSubSize) {
9755             /* Layout does not admit a pointwise block size */
9756             bs = 1;
9757           }
9758         }
9759       }
9760     }
9761     /* Must have same blocksize on all procs (some might have no points) */
9762     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs; bsLocal[1] = bs;
9763     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject) dm), bsLocal, bsMinMax));
9764     if (bsMinMax[0] != bsMinMax[1]) {bs = 1;}
9765     else                            {bs = bsMinMax[0];}
9766     PetscCall(PetscMalloc1(subSize, &subIndices));
9767     for (p = pStart; p < pEnd; ++p) {
9768       PetscInt gdof, goff;
9769 
9770       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
9771       if (gdof > 0) {
9772         const PetscInt point = spmap[p];
9773 
9774         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
9775         for (f = 0; f < Nf; ++f) {
9776           PetscInt fdof, fcdof, fc, f2, poff = 0;
9777 
9778           /* Can get rid of this loop by storing field information in the global section */
9779           for (f2 = 0; f2 < f; ++f2) {
9780             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
9781             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
9782             poff += fdof-fcdof;
9783           }
9784           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
9785           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
9786           for (fc = 0; fc < fdof-fcdof; ++fc, ++subOff) {
9787             subIndices[subOff] = goff+poff+fc;
9788           }
9789         }
9790       }
9791     }
9792     PetscCall(ISRestoreIndices(spIS, &spmap));
9793     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
9794     if (bs > 1) {
9795       /* We need to check that the block size does not come from non-contiguous fields */
9796       PetscInt i, j, set = 1;
9797       for (i = 0; i < subSize; i += bs) {
9798         for (j = 0; j < bs; ++j) {
9799           if (subIndices[i+j] != subIndices[i]+j) {set = 0; break;}
9800         }
9801       }
9802       if (set) PetscCall(ISSetBlockSize(*is, bs));
9803     }
9804     /* Attach nullspace */
9805     for (f = 0; f < Nf; ++f) {
9806       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
9807       if ((*subdm)->nullspaceConstructors[f]) break;
9808     }
9809     if (f < Nf) {
9810       MatNullSpace nullSpace;
9811       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
9812 
9813       PetscCall(PetscObjectCompose((PetscObject) *is, "nullspace", (PetscObject) nullSpace));
9814       PetscCall(MatNullSpaceDestroy(&nullSpace));
9815     }
9816   }
9817   PetscFunctionReturn(0);
9818 }
9819 
9820 /*@
9821   DMPlexMonitorThroughput - Report the cell throughput of FE integration
9822 
9823   Input Parameter:
9824 - dm - The DM
9825 
9826   Level: developer
9827 
9828   Options Database Keys:
9829 . -dm_plex_monitor_throughput - Activate the monitor
9830 
9831 .seealso: `DMSetFromOptions()`, `DMPlexCreate()`
9832 @*/
9833 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
9834 {
9835 #if defined(PETSC_USE_LOG)
9836   PetscStageLog      stageLog;
9837   PetscLogEvent      event;
9838   PetscLogStage      stage;
9839   PetscEventPerfInfo eventInfo;
9840   PetscReal          cellRate, flopRate;
9841   PetscInt           cStart, cEnd, Nf, N;
9842   const char        *name;
9843 #endif
9844 
9845   PetscFunctionBegin;
9846   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9847 #if defined(PETSC_USE_LOG)
9848   PetscCall(PetscObjectGetName((PetscObject) dm, &name));
9849   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9850   PetscCall(DMGetNumFields(dm, &Nf));
9851   PetscCall(PetscLogGetStageLog(&stageLog));
9852   PetscCall(PetscStageLogGetCurrent(stageLog, &stage));
9853   PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
9854   PetscCall(PetscLogEventGetPerfInfo(stage, event, &eventInfo));
9855   N        = (cEnd - cStart)*Nf*eventInfo.count;
9856   flopRate = eventInfo.flops/eventInfo.time;
9857   cellRate = N/eventInfo.time;
9858   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)));
9859 #else
9860   SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Plex Throughput Monitor is not supported if logging is turned off. Reconfigure using --with-log.");
9861 #endif
9862   PetscFunctionReturn(0);
9863 }
9864