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